[
  {
    "path": ".dockerignore",
    "content": "# Files\n/**/*/.env\n/**/*/.env.*\n/**/*/README.md\n/**/*/.git\n/**/*/.github\n/**/*/.gitignore\n/**/*/.dockerignore\n/**/*/.turbo\n/**/*/npm-debug.log\n/**/*/Dockerfile\n\n# Directories\n/**/*/node_modules\n/**/*/test\n/**/*/dist\n\n# Auth.js Dirs\napps/*\ndocs/**\npackages/**\n!packages/next-auth\n!packages/core\n!packages/utils\n!apps/examples/nextjs-docker\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "# Learn how to add code owners here:\n# https://help.github.com/en/articles/about-code-owners\n\n*                     @balazsorban44\n.github               @ThangHuuVu @ndom91\n/apps/                @ubbe-xyz @ndom91 @ThangHuuVu\n/docs/                @ubbe-xyz @ndom91\n/packages/            @ThangHuuVu\n/packages/adapter-*/  @ndom91\n/**/*test*            @ubbe-xyz\n/**/*type*            @ubbe-xyz\n"
  },
  {
    "path": ".github/DISCUSSION_TEMPLATE/ideas.yml",
    "content": "body:\n  - type: textarea\n    attributes:\n      label: Goals\n      description: Short list of what the feature request aims to address?\n      value: |\n        1.\n        2.\n        3.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Non-Goals\n      description: Short list of what the feature request _does not_ aim to address?\n      value: |\n        1.\n        2.\n        3.\n    validations:\n      required: false\n  - type: textarea\n    attributes:\n      label: Background\n      description: Discuss prior art, why do you think this feature is needed? Are there current alternatives?\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Proposal\n      description: How should this feature be implemented? Are you interested in contributing?\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/DISCUSSION_TEMPLATE/questions.yml",
    "content": "body:\n  - type: textarea\n    attributes:\n      label: Summary\n      description: What do you need help with?\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Additional information\n      description: Any code snippets, error messages, or dependency details that may be related?\n      render: js\n    validations:\n      required: false\n  - type: input\n    attributes:\n      label: Example\n      description: A link to a minimal reproduction is helpful for collaborative debugging!\n    validations:\n      required: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1_bug_framework.yml",
    "content": "name: Bug report\ndescription: Report an issue so we can improve\nlabels: [triage, bug]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        **NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.\n        Thanks for taking the time to fill out this issue after reading/searching through the [documentation](https://next-auth.js.org) first!\n        Is this your first time contributing? Check out this video: https://www.youtube.com/watch?v=cuoNzXFLitc\n\n        ### Important :exclamation:\n\n        _Providing incorrect/insufficient information or skipping steps to reproduce the issue *will* result in closing the issue or converting to a discussion without further explanation._\n\n        If you have a generic question specific to your project, it is best asked in Discussions under the [Questions category](https://github.com/nextauthjs/next-auth/discussions/new?category=Questions)\n  # Let's wait with this until adoption in other frameworks.\n  # - type: dropdown\n  #   attributes:\n  #     label: Framework\n  #     description: Which framework(s) is this issue related to?\n  #     multiple: true\n  #     options:\n  #       - \"Next.js\"\n  #       - \"Other\"\n  - type: textarea\n    attributes:\n      label: Environment\n      description: |\n        Run this command in your project's root folder and paste the result:\n\n        `npx envinfo --system --binaries --browsers --npmPackages \"{next,react,next-auth,@auth/*}\"`\n\n        Alternatively, you can manually gather the version information from your package.json for these packages: \"next\", \"react\" and \"next-auth\". Please also mention your OS and Node.js version, as well as the browser you are using.\n      value: |\n        ```\n        Paste here\n        ```\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Reproduction URL\n      description: A URL to a public github.com repository outside the next-auth org that clearly reproduces your issue. You can use our [`next-auth-example`](https://github.com/nextauthjs/next-auth-example) template repository to get started more easily\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Describe the issue\n      description: Describe us what the issue is and what have you tried so far to fix it. Add any extra useful information in this section. Feel free to use screenshots (but prefer [code blocks](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) over a picture of your code) or a video explanation.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: How to reproduce\n      description: Explain with clear steps how to reproduce the issue\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Expected behavior\n      description: Explain what should have happened instead of what actually happened\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/2_bug_provider.yml",
    "content": "name: Bug report (Provider)\ndescription: Create a provider-specific report\nlabels: [triage, bug, providers]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        **NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.\n        Thanks for taking the time to fill out this [Provider](https://next-auth.js.org/providers/overview) related issue!\n        Is this your first time contributing? Check out this video: https://www.youtube.com/watch?v=cuoNzXFLitc\n\n        ### Important :exclamation:\n\n        _Providing incorrect/insufficient information or skipping steps to reproduce the issue *will* result in closing the issue or converting to a discussion without further explanation._\n\n        If you have a generic question specific to your project, it is best asked in Discussions under the [Questions category](https://github.com/nextauthjs/next-auth/discussions/new?category=Questions)\n  - type: dropdown\n    attributes:\n      label: Provider type\n      description: Provider(s) this issue is related to\n      multiple: true\n      options:\n        - \"Credentials\"\n        - \"Email\"\n        - \"Custom provider\"\n        - \"42 School\"\n        - \"Apple\"\n        - \"Asgardeo\"\n        - \"Atlassian\"\n        - \"Auth0\"\n        - \"Authentik\"\n        - \"Azure Active Directory\"\n        - \"Azure Active Directory B2C\"\n        - \"Azure DevOps\"\n        - \"Battlenet\"\n        - \"Beyond Identity\"\n        - \"Bitbucket\"\n        - \"Box\"\n        - \"Bungie\"\n        - \"ClickUp\"\n        - \"Cognito\"\n        - \"Concept2\"\n        - \"Coinbase\"\n        - \"Descope\"\n        - \"Discord\"\n        - \"Dribbble\"\n        - \"Dropbox\"\n        - \"Eventbrite\"\n        - \"EVE Online\"\n        - \"Facebook\"\n        - \"FACEIT\"\n        - \"Figma\"\n        - \"Foursquare\"\n        - \"Freshbooks\"\n        - \"FusionAuth\"\n        - \"GitHub\"\n        - \"GitLab\"\n        - \"Google\"\n        - \"Hugging Face\"\n        - \"Identity Server 4\"\n        - \"Instagram\"\n        - \"Kakao\"\n        - \"Frontegg\"\n        - \"Keycloak\"\n        - \"Kinde\"\n        - \"Line\"\n        - \"LinkedIn\"\n        - \"Logto\"\n        - \"Loops\"\n        - \"Mailchimp\"\n        - \"Mail.ru\"\n        - \"Mastodon\"\n        - \"Medium\"\n        - \"Microsoft Entra ID\"\n        - \"Naver\"\n        - \"Netlify\"\n        - \"NetSuite\"\n        - \"Nextcloud\"\n        - \"Notion\"\n        - \"Okta\"\n        - \"OneLogin\"\n        - \"Osso\"\n        - \"Osu\"\n        - \"Patreon\"\n        - \"Ping Identity\"\n        - \"Pipedrive\"\n        - \"Reddit\"\n        - \"Roblox\"\n        - \"Salesforce\"\n        - \"SimpleLogin\"\n        - \"Slack\"\n        - \"Spotify\"\n        - \"Strava\"\n        - \"Threads\"\n        - \"Tiktok\"\n        - \"Todoist\"\n        - \"Trakt\"\n        - \"Twitch\"\n        - \"Twitter\"\n        - \"Vk\"\n        - \"Webex\"\n        - \"Wordpress\"\n        - \"WorkOS\"\n        - \"Yandex\"\n        - \"Zoho\"\n        - \"Zoom\"\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Environment\n      description: |\n        Run this command in your project's root folder and paste the result:\n\n        `npx envinfo --system --binaries --browsers --npmPackages \"{next,react,next-auth,@auth/*}\"`\n\n        Alternatively, you can manually gather the version information from your package.json for these packages: \"next\", \"react\" and \"next-auth\". Please also mention your OS and Node.js version, as well as the browser you are using.\n      value: |\n        ```\n        Paste here\n        ```\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Reproduction URL\n      description: A URL to a public github.com repository outside the next-auth org that clearly reproduces your issue. You can use our [`next-auth-example`](https://github.com/nextauthjs/next-auth-example) template repository to get started more easily\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Describe the issue\n      description: Describe us what the issue is and what have you tried so far to fix it. Add any extra useful information in this section. Feel free to use screenshots (but prefer [code blocks](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) over a picture of your code) or a video explanation.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: How to reproduce\n      description: Explain with clear steps how to reproduce the issue\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Expected behavior\n      description: Explain what should have happened instead of what actually happened\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/3_bug_adapter.yml",
    "content": "name: Bug report (Adapter)\ndescription: Create an adapter-specific report\nlabels: [triage, bug, adapters]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        **NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.\n        Thanks for taking the time to fill out this [Adapter](https://next-auth.js.org/getting-started/adapters) related issue!\n        Is this your first time contributing? Check out this video: https://www.youtube.com/watch?v=cuoNzXFLitc\n\n        ### Important :exclamation:\n\n        _Providing incorrect/insufficient information or skipping steps to reproduce the issue *will* result in closing the issue or converting to a discussion without further explanation._\n\n        If you have a generic question specific to your project, it is best asked in Discussions under the [Questions category](https://github.com/nextauthjs/next-auth/discussions/new?category=Questions)\n  - type: dropdown\n    attributes:\n      label: Adapter type\n      description: Adapter(s) this issue is related to\n      multiple: true\n      options:\n        - \"Custom adapter\"\n        - \"@auth/azure-tables-adapter\"\n        - \"@auth/edgedb-adapter\"\n        - \"@auth/d1-adapter\"\n        - \"@auth/dgraph-adapter\"\n        - \"@auth/drizzle-adapter\"\n        - \"@auth/dynamodb-adapter\"\n        - \"@auth/fauna-adapter\"\n        - \"@auth/firebase-adapter\"\n        - \"@auth/hasura-adapter\"\n        - \"@auth/kysely-adapter\"\n        - \"@auth/mikro-orm-adapter\"\n        - \"@auth/mongodb-adapter\"\n        - \"@auth/neo4j-adapter\"\n        - \"@auth/pg-adapter\"\n        - \"@auth/pouchdb-adapter\"\n        - \"@auth/prisma-adapter\"\n        - \"@auth/sequelize-adapter\"\n        - \"@auth/supabase-adapter\"\n        - \"@auth/surrealdb-adapter\"\n        - \"@auth/typeorm-adapter\"\n        - \"@auth/unstorage-adapter\"\n        - \"@auth/upstash-redis-adapter\"\n        - \"@auth/xata-adapter\"\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Environment\n      description: |\n        Run this command in your project's root folder and paste the result:\n\n        `npx envinfo --system --binaries --browsers --npmPackages \"{next,react,next-auth,@auth/*}\"`\n\n        Alternatively, you can manually gather the version information from your package.json for these packages: \"next\", \"react\" and \"next-auth\". Please also mention your OS and Node.js version, as well as the browser you are using.\n      value: |\n        ```\n        Paste here\n        ```\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Reproduction URL\n      description: A URL to a public github.com repository outside the next-auth org that clearly reproduces your issue. You can use our [`next-auth-example`](https://github.com/nextauthjs/next-auth-example) template repository to get started more easily\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Describe the issue\n      description: Describe us what the issue is and what have you tried so far to fix it. Add any extra useful information in this section. Feel free to use screenshots (but prefer [code blocks](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) over a picture of your code) or a video explanation.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: How to reproduce\n      description: Explain with clear steps how to reproduce the issue\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Expected behavior\n      description: Explain what should have happened instead of what actually happened\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/4_documentation.yml",
    "content": "name: \"Documentation\"\ndescription: Request to update or improve NextAuth.js documentation\nlabels: [\"triage\", \"documentation\"]\nbody:\n  - type: textarea\n    attributes:\n      label: What is the improvement or update you wish to see?\n      description: \"Example: The `next-auth` docs are missing information about X.\"\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Is there any context that might help us understand?\n      description: A clear description of any added context that might help us understand.\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Does the docs page already exist? Please link to it.\n      description: \"Example: https://next-auth.js.org/getting-started/introduction\"\n    validations:\n      required: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Get help from the community (Discord)\n    url: https://discord.authjs.dev\n    about: Ask questions and discuss with other community members\n  - name: Ask a question\n    url: https://github.com/nextauthjs/next-auth/discussions/new?category=questions\n    about: Ask questions and discuss with other community members\n  - name: Feature request\n    url: https://github.com/nextauthjs/next-auth/discussions/new?category=ideas\n    about: Feature requests should be opened as discussions\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nThanks for your interest in the project. Bugs filed and PRs submitted are appreciated!\n\nPlease fill out the information below to expedite the review and (hopefully)\nmerge of your pull request!\n\n**NOTE**:\n\n- It's a good idea to open an issue first to discuss potential changes.\n- Please make sure that you are _NOT_ opening a PR to fix a potential security vulnerability. Instead, please follow the [Security guidelines](https://github.com/nextauthjs/.github/blob/main/SECURITY.md) to disclose the issue to us confidentially.\n\n-->\n\n## ☕️ Reasoning\n\n<!-- What changes are being made? What feature/bug is being fixed here? -->\n\n## 🧢 Checklist\n\n- [ ] Documentation\n- [ ] Tests\n- [ ] Ready to be merged\n\n## 🎫 Affected issues\n\n<!--\nPlease [scout and link issues](https://github.com/nextauthjs/next-auth/issues) that might be solved by this PR. And include text like the following to close them automatically when this is merged:\n\nFixes: INSERT_ISSUE_LINK_HERE\n-->\n\n## 📌 Resources\n\n- [Security guidelines](https://github.com/nextauthjs/.github/blob/main/SECURITY.md)\n- [Contributing guidelines](https://github.com/nextauthjs/.github/blob/main/CONTRIBUTING.md)\n- [Code of conduct](https://github.com/nextauthjs/.github/blob/main/CODE_OF_CONDUCT.md)\n- [Contributing to Open Source](https://kcd.im/pull-request)\n"
  },
  {
    "path": ".github/broken-link-checker/action.yml",
    "content": "name: \"Broken Link Checker\"\ndescription: \"Recursively checks input URL for broken links\"\noutputs:\n  version:\n    description: \"Check for broken internal links\"\nruns:\n  using: \"node20\"\n  main: \"dist/index.js\"\n"
  },
  {
    "path": ".github/broken-link-checker/index.d.ts",
    "content": "declare module \"broken-link-checker\";\n"
  },
  {
    "path": ".github/broken-link-checker/package.json",
    "content": "{\n  \"name\": \"broken-link-checker\",\n  \"private\": true,\n  \"version\": \"0.2.0\",\n  \"description\": \"Find broken links as a GitHub Action\",\n  \"main\": \"dist/index.js\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"tsx src/index.ts\",\n    \"build\": \"npx tsup --clean --minify --format esm src/index.ts\",\n    \"types\": \"tsc\"\n  },\n  \"keywords\": [\n    \"typescript\",\n    \"broken-link-checker\",\n    \"github-action\"\n  ],\n  \"author\": \"ndom91 <yo@ndo.dev> (https://ndo.dev/)\",\n  \"license\": \"MIT\",\n  \"devDependencies\": {\n    \"@types/node\": \"^20.11.15\",\n    \"tsup\": \"^8.0.1\",\n    \"tsx\": \"^4.7.0\",\n    \"typescript\": \"^5.3.3\"\n  },\n  \"dependencies\": {\n    \"@actions/core\": \"^1.10.1\",\n    \"@actions/github\": \"^6.0.0\",\n    \"broken-link-checker\": \"^0.7.8\"\n  }\n}\n"
  },
  {
    "path": ".github/broken-link-checker/src/index.ts",
    "content": "import blc from \"broken-link-checker\"\nimport { setFailed } from \"@actions/core\"\nimport * as github from \"@actions/github\"\n\ntype TODO = any\ntype Output = {\n  errors: any[]\n  links: any[]\n  pages: any[]\n  sites: any[]\n}\ntype Comment = {\n  id: number\n}\ntype FindBotComment = {\n  octokit: TODO\n  owner: string\n  repo: string\n  prNumber: number\n}\n\nconst COMMENT_TAG = \"# Broken Link Checker\"\n\nasync function findBotComment({\n  octokit,\n  owner,\n  repo,\n  prNumber,\n}: FindBotComment): Promise<Comment | undefined> {\n  try {\n    const { data: comments } = await octokit.rest.issues.listComments({\n      owner,\n      repo,\n      issue_number: prNumber,\n    })\n\n    return comments.find((c: TODO) => c.body?.includes(COMMENT_TAG))\n  } catch (error) {\n    setFailed(\"Error finding bot comment: \" + error)\n    return undefined\n  }\n}\n\nasync function updateCheckStatus(\n  brokenLinkCount: number,\n  commentUrl?: string\n): Promise<void> {\n  const checkName = \"Broken Link Checker\"\n  const summary = `Found ${brokenLinkCount} broken links in this PR. Click details for a list.`\n  const text = `[See the comment for details](${commentUrl})`\n  const { context, getOctokit } = github\n  const octokit = getOctokit(process.env.GITHUB_TOKEN!)\n  const { owner, repo } = context.repo\n\n  // Can only update status on 'pull_request' events\n  if (context.payload.pull_request) {\n    const pullRequest = context.payload.pull_request\n    const sha = pullRequest?.head.sha\n\n    const checkParams = {\n      owner,\n      repo,\n      name: checkName,\n      head_sha: sha,\n      status: \"completed\" as const,\n      conclusion: \"failure\" as const,\n      output: {\n        title: checkName,\n        summary: summary,\n        text: text,\n      },\n    }\n\n    try {\n      await octokit.rest.checks.create(checkParams)\n    } catch (error) {\n      setFailed(\"Failed to create check: \" + error)\n    }\n  }\n}\n\nconst postComment = async (\n  outputMd: string,\n  brokenLinkCount: number = 0\n): Promise<string> => {\n  try {\n    const { context, getOctokit } = github\n    const octokit = getOctokit(process.env.GITHUB_TOKEN!)\n    const { owner, repo } = context.repo\n    let prNumber\n\n    // Handle various trigger events\n    if (context.payload.pull_request) {\n      // Triggered by `pull_request`\n      prNumber = context.payload.pull_request?.number\n    } else if (context.payload.issue) {\n      // Triggered by `issue_comment`\n      prNumber = context.payload?.issue?.number\n    }\n\n    if (!prNumber) {\n      setFailed(\"Count not find PR Number\")\n      return \"\"\n    }\n\n    const botComment = await findBotComment({\n      octokit,\n      owner,\n      repo,\n      prNumber,\n    })\n    if (botComment) {\n      console.log(\"Updating Comment\")\n      const { data } = await octokit.rest.issues.updateComment({\n        owner,\n        repo,\n        comment_id: botComment?.id,\n        body: outputMd,\n      })\n\n      return data.html_url\n    } else if (brokenLinkCount > 0) {\n      console.log(\"Creating Comment\")\n      const { data } = await octokit.rest.issues.createComment({\n        owner,\n        repo,\n        issue_number: prNumber,\n        body: outputMd,\n      })\n      return data.html_url\n    }\n    return \"\"\n  } catch (error) {\n    setFailed(\"Error commenting: \" + error)\n    return \"\"\n  }\n}\n\nconst generateOutputMd = (output: Output): string => {\n  // Add comment header\n  let outputMd = `${COMMENT_TAG}\n\n> **${output.links.length}** broken links found. Links organised below by source page, or page where they were found.\n`\n\n  // Build map of page and array of its found broken links\n  const linksByPage = output.links.reduce((acc, link) => {\n    if (!acc[link.base.resolved]) {\n      acc[link.base.resolved] = []\n      acc[link.base.resolved].push(link)\n    } else {\n      acc[link.base.resolved].push(link)\n    }\n    return acc\n  }, {})\n\n  // Write out markdown tables of these links\n  Object.entries(linksByPage).forEach(([page, links], i) => {\n    outputMd += `\n\n### ${i + 1}) [${new URL(page).pathname}](${page})\n\n| Target Link | Link Text  |\n|------|------|\n`\n\n    // @ts-expect-error\n    links.forEach((link: TODO) => {\n      outputMd += `| [${new URL(link.url.resolved).pathname}](${\n        link.url.resolved\n      }) | \"${link.html?.text?.trim().replaceAll(\"\\n\", \"\")}\" |\n`\n    })\n  })\n\n  // If there were scrape errors, append to bottom of comment\n  if (output.errors.length) {\n    outputMd += `\n### Errors\n`\n    output.errors.forEach((error) => {\n      outputMd += `\n${error}\n`\n    })\n  }\n\n  return outputMd\n}\n\n// Main function that triggers link validation across .mdx files\nasync function brokenLinkChecker(): Promise<void> {\n  if (!process.env.GITHUB_TOKEN) {\n    throw new Error(\"GITHUB_TOKEN is required\")\n  }\n  const siteUrl =\n    process.env.VERCEL_PREVIEW_URL || \"https://authjs-nextra-docs.vercel.app\"\n  const output: Output = {\n    errors: [],\n    links: [],\n    pages: [],\n    sites: [],\n  }\n\n  const options = {\n    excludeExternalLinks: true,\n    honorRobotExclusions: false,\n    filterLevel: 0,\n    excludedKeywords: [],\n  }\n\n  const siteChecker = new blc.SiteChecker(options, {\n    error: (error: TODO) => {\n      output.errors.push(error)\n    },\n    link: (result: TODO) => {\n      if (result.broken) {\n        output.links.push(result)\n      }\n    },\n    end: async () => {\n      if (output.links.length) {\n        // DEBUG\n        // console.debug(output.links)\n\n        // Skip links that returned 308\n        const brokenLinksForAttention = output.links.filter(\n          (link) => link.broken && ![\"HTTP_308\"].includes(link.brokenReason)\n        )\n\n        const outputMd = generateOutputMd({\n          errors: output.errors,\n          links: brokenLinksForAttention,\n          pages: [],\n          sites: [],\n        })\n        const commentUrl = await postComment(\n          outputMd,\n          brokenLinksForAttention.length\n        )\n\n        // Update GitHub \"check\" status\n        await updateCheckStatus(brokenLinksForAttention.length, commentUrl)\n\n        brokenLinksForAttention.length && setFailed(`Found broken links`)\n      }\n    },\n  })\n\n  siteChecker.enqueue(siteUrl)\n}\n\nbrokenLinkChecker()\n"
  },
  {
    "path": ".github/broken-link-checker/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"noEmit\": true,\n    \"target\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"rootDir\": \"./src\",\n    \"types\": [\"./index.d.ts\", \"node\"],\n    \"strict\": true,\n    \"noImplicitAny\": true,\n    \"allowSyntheticDefaultImports\": true\n  }\n}\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"npm\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"weekly\"\n    versioning-strategy: \"increase\"\n    allow:\n      - dependency-name: \"oauth4webapi\"\n      - dependency-name: \"jose\"\n"
  },
  {
    "path": ".github/good-first-issue.md",
    "content": "This issue was marked with the `good first issue` label by a maintainer.\n\nThis means that it is a good candidate for someone interested in contributing to the project, but does not know where to start.\n\nHave a look at the [Contributing Guide](https://github.com/nextauthjs/.github/blob/main/CONTRIBUTING.md) first.\n\nThis will help you set up your development environment to get started. When you are ready, open a PR, and link back to this issue in the form of adding `Fixes #1234` to the PR description, where `1234` is the issue number. This will auto-close the issue when the PR gets merged, making it easier for us to keep track of what has been fixed.\n\nPlease make sure that - if applicable - you add tests for the changes you make.\n\nIf you have any questions, feel free to ask in the comments below or the PR. Generally, you don't need to `@mention` anyone directly, as we will get notified anyway and will respond as soon as we can)\n\n> [!NOTE]  \n> There is no need to ask for permission \"can I work on this?\" Please, go ahead if there is no linked PR :slightly_smiling_face:\n"
  },
  {
    "path": ".github/help-needed.md",
    "content": "This issue was marked with the `help needed` label by a maintainer.\n\nThe issue might require some digging, so it is recommended to have some experience with the project.\n\nHave a look at the [Contributing Guide](https://github.com/nextauthjs/.github/blob/main/CONTRIBUTING.md) first.\n\nThis will help you set up your development environment to get started. When you are ready, open a PR, and link back to this issue in the form of adding `Fixes #1234` to the PR description, where `1234` is the issue number. This will auto-close the issue when the PR gets merged, making it easier for us to keep track of what has been fixed.\n\nPlease make sure that - if applicable - you add tests for the changes you make.\n\nIf you have any questions, feel free to ask in the comments below or the PR. Generally, you don't need to `@mention` anyone directly, as we will get notified anyway and will respond as soon as we can)\n\n> [!NOTE]  \n> There is no need to ask for permission \"can I work on this?\" Please, go ahead if there is no linked PR :slightly_smiling_face:\n"
  },
  {
    "path": ".github/invalid-reproduction.md",
    "content": "We could not detect a valid reproduction link. **Make sure to follow the bug report template carefully.**\n\n### Why was this issue closed?\n\nTo be able to investigate, we need access to a reproduction to identify what triggered the issue. We need a link to a **public** GitHub repository. Example: ([NextAuth.js example repository](https://github.com/nextauthjs/next-auth-example)).\n\nThe bug template that you filled out has a section called \"Reproduction URL\", which is where you should provide the link to the reproduction.\n\n- If you did not provide a link or the link you provided is not hosted on github.com outside of the next-auth organization, we will close the issue.\n- If you provide a link to a private repository, we will close the issue.\n- If you provide a link to a repository but not in the correct section, we will close the issue.\n\n### What should I do?\n\nDepending on the reason the issue was closed, you can do the following:\n\n- If you did not provide a link hosted on github.com outside of the next-auth organization, please open a new issue with a link to such a reproduction.\n- If you provided a link to a private repository, please open a new issue with a link to a public repository.\n- If you provided a link to a repository but not in the correct section, please open a new issue with a link to a reproduction in the correct section.\n\n**In general, assume that we should not go through a lengthy onboarding process at your company code only to be able to verify an issue.**\n\n### My repository is private and cannot make it public\n\nIn most cases, a private repo will not be a sufficient **minimal reproduction**, as this codebase might contain a lot of unrelated parts that would make our investigation take longer. Please do **not** make it public. Instead, create a new repository using the templates above, adding the relevant code to reproduce the issue. Common things to look out for:\n\n- Remove any code that is not related to the issue. (pages, API Routes, components, etc.)\n- Remove any dependencies that are not related to the issue.\n- Remove any third-party service that would require us to sign up for an account to reproduce the issue.\n- Remove any environment variables that are not related to the issue.\n- Remove private packages that we do not have access to.\n- If the issue is not related to a monorepo specifically, try to reproduce the issue without a complex monorepo setup\n\n### I did not open this issue, but it is relevant to me, what can I do to help?\n\nAnyone experiencing the same issue is welcome to provide a minimal reproduction following the above steps by opening a new issue.\n\n### I think my reproduction is good enough, why aren't you looking into it quickly?\n\nWe look into every issue and monitor open issues for new comments.\n\nHowever, sometimes we might miss a few due to the popularity/high traffic of the repository. We apologize, and kindly ask you to refrain from tagging core maintainers, as that will usually not result in increased priority.\n\nUpvoting issues to show your interest will help us prioritize and address them as quickly as possible. That said, every issue is important to us, and if an issue gets closed by accident, we encourage you to open a new one linking to the old issue and we will look into it.\n\n### Useful Resources\n\n- [How to create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve)\n- [Bug report: Framework](https://github.com/nextauthjs/next-auth/issues/new?assignees=&labels=triage&projects=&template=1_bug_framework.yml)\n- [Bug report: Provider](https://github.com/nextauthjs/next-auth/issues/new?assignees=&labels=triage%2Cproviders&projects=&template=2_bug_provider.yml)\n- [Bug report: Adapter](https://github.com/nextauthjs/next-auth/issues/new?assignees=&labels=triage%2Cadapters&projects=&template=3_bug_adapter.yml)\n"
  },
  {
    "path": ".github/pr-labeler.yml",
    "content": "# https://github.com/actions/labeler#create-githublabeleryml\nadapters: [\"packages/core/src/adapters.ts\", \"packages/adapter-*/**/*\"]\ncore: [\"packages/core/src/**/*\"]\nazure-tables: [\"packages/adapter-azure-tables/**/*\"]\nedgedb: [\"packages/adapter-edgedb/**/*\"]\nd1: [\"packages/adapter-d1/**/*\"]\ndgraph: [\"packages/adapter-dgraph/**/*\"]\ndrizzle: [\"packages/adapter-drizzle/**/*\"]\ndocumentation: [\"packages/docs/docs/**/*\"]\ndynamodb: [\"packages/adapter-dynamodb/**/*\"]\nexamples: [\"apps/examples/**/*\"]\nexpress: [\"packages/frameworks-express/**/*\"]\nfauna: [\"packages/adapter-fauna/**/*\"]\nfirebase: [\"packages/adapter-firebase/**/*\"]\nhasura: [\"packages/adapter-hasura/**/*\"]\nframeworks: [\"packages/frameworks-*/**/*\"]\nmikro-orm: [\"packages/adapter-mikro-orm/**/*\"]\nmongodb: [\"packages/adapter-mongodb/**/*\"]\nneo4j: [\"packages/adapter-neo4j/**/*\"]\nnext-auth: [\"packages/next-auth/**/*\"]\npg: [\"packages/adapter-pg/**/*\"]\nneon: [\"packages/adapter-neon/**/*\"]\nplaygrounds: [\"apps/playgrounds/**/*\"]\npouchdb: [\"packages/adapter-pouchdb/**/*\"]\nprisma: [\"packages/adapter-prisma/**/*\"]\nkysely: [\"packages/adapter-kysely/**/*\"]\nproviders: [\"packages/core/src/providers/**/*\"]\nsequelize: [\"packages/adapter-sequelize/**/*\"]\nsolidjs: [\"packages/frameworks-solid-start/**/*\"]\nsupabase: [\"packages/adapter-supabase/**/*\"]\nsurrealdb: [\"packages/adapter-surrealdb/**/*\"]\nsvelte: [\"packages/frameworks-sveltekit/**/*\"]\ntest: [\"**test**/*\"]\ntypeorm: [\"packages/adapter-typeorm/**/*\"]\nunstorage: [\"packages/adapter-unstorage/**/*\"]\nupstash-redis: [\"packages/adapter-upstash-redis/**/*\"]\nxata: [\"packages/adapter-xata/**/*\"]\n"
  },
  {
    "path": ".github/stale.yml",
    "content": "# https://github.com/probot/stale#usage\n\ndaysUntilStale: 60\ndaysUntilClose: 7\nexemptLabels:\n  - pinned\n  - security\n  - priority\n  - bug\n  - triage\n  - accepted\nstaleLabel: stale\nonly: issues\nmarkComment: >\n  It looks like this issue did not receive any activity for 60 days.\n  It will be closed in 7 days if no further activity occurs. If you think your issue\n  is still relevant, commenting will keep it open. Thanks!\ncloseComment: >\n  To keep things tidy, we are closing this issue for now.\n  If you think your issue is still relevant, leave a comment\n  and we might reopen it. Thanks!\n"
  },
  {
    "path": ".github/sync.yml",
    "content": "nextauthjs/express-auth-example:\n  - source: apps/examples/express\n    dest: .\n    deleteOrphaned: true\n  - .github/FUNDING.yml\n  - LICENSE\n\nnextauthjs/sveltekit-auth-example:\n  - source: apps/examples/sveltekit\n    dest: .\n    deleteOrphaned: true\n  - .github/FUNDING.yml\n  - LICENSE\n\nnextauthjs/solid-start-auth-example:\n  - source: \"apps/examples/solid-start\"\n    dest: .\n    deleteOrphaned: true\n  - .github/FUNDING.yml\n  - LICENSE\n\nnextauthjs/next-auth-example:\n  - source: apps/examples/nextjs\n    dest: .\n    deleteOrphaned: true\n  - .github/FUNDING.yml\n  - LICENSE\n\nnextauthjs/next-auth-pages-example:\n  - source: apps/examples/nextjs-pages\n    dest: .\n    deleteOrphaned: true\n  - .github/FUNDING.yml\n  - LICENSE\n\nnextauthjs/qwik-auth-example:\n  - source: apps/examples/qwik\n    dest: .\n    deleteOrphaned: true\n  - .github/FUNDING.yml\n  - LICENSE\n"
  },
  {
    "path": ".github/version-pr/action.yml",
    "content": "name: \"Determine version\"\ndescription: \"Determines npm package version based on PR number and commit SHA\"\noutputs:\n  version:\n    description: \"npm package version\"\nruns:\n  using: \"node20\"\n  main: \"index.js\"\n"
  },
  {
    "path": ".github/version-pr/index.js",
    "content": "const fs = require(\"fs\")\nconst path = require(\"path\")\nconst core = require(\"@actions/core\")\n\ntry {\n  const packageJSONPath = path.join(\n    process.cwd(),\n    `packages/${process.env.PACKAGE_PATH || \"next-auth\"}/package.json`\n  )\n  const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, \"utf8\"))\n\n  const sha8 = process.env.GITHUB_SHA.substring(0, 8)\n  const prefix = \"0.0.0-\"\n  const pr = process.env.PR_NUMBER\n  const source = pr ? `pr.${pr}` : \"manual\"\n  const packageVersion = `${prefix}${source}.${sha8}`\n  packageJSON.version = packageVersion\n  core.setOutput(\"version\", packageVersion)\n  fs.writeFileSync(packageJSONPath, JSON.stringify(packageJSON))\n} catch (error) {\n  core.setFailed(error.message)\n}\n"
  },
  {
    "path": ".github/workflows/broken-link-checker.yml",
    "content": "name: \"Broken Link Checker\"\n\non:\n  issue_comment:\n    types: [edited]\n\npermissions:\n  pull-requests: write\n  checks: write\n\njobs:\n  broken-link-checker:\n    runs-on: ubuntu-latest\n    if: github.actor == 'vercel[bot]'\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n      - run: corepack enable\n      - uses: aaimio/vercel-preview-url-action@v2.2.0\n        id: vercel_preview_url\n        with:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          preview_url_regexp: https.*\\/(.*-authjs.vercel.app)\n      - name: Install dependencies\n        run: cd ./.github/broken-link-checker && pnpm install --ignore-workspace && pnpm build\n      - name: Run link checker\n        uses: ./.github/broken-link-checker\n        id: broken-links\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          VERCEL_PREVIEW_URL: https://${{ steps.vercel_preview_url.outputs.vercel_preview_url }}\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "name: Code Analysis\n\non:\n  push:\n    branches: [beta, next]\n  pull_request:\n    branches: [main]\n  schedule:\n    - cron: \"43 17 * * 2\"\n\njobs:\n  analyze:\n    name: Verify\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [\"javascript\"]\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v3\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@v2\n        with:\n          languages: ${{ matrix.language }}\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@v2\n"
  },
  {
    "path": ".github/workflows/pr-labeler.yml",
    "content": "# https://github.com/actions/labeler#create-workflow\n\nname: Label Pull Requests\n\non:\n  pull_request_target:\n\njobs:\n  prs:\n    name: Triage\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/labeler@v4\n        with:\n          repo-token: \"${{ secrets.GITHUB_TOKEN }}\"\n          configuration-path: \".github/pr-labeler.yml\"\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  push:\n    branches:\n      - main\n      - beta\n      - next\n      - 3.x\n  pull_request:\n  merge_group:\n  # TODO: Support latest releases\n  workflow_dispatch:\n    inputs:\n      name:\n        type: choice\n        description: Package name (npm)\n        options:\n          - \"next-auth\"\n          - \"@auth/core\"\n          - \"@auth/express\"\n          - \"@auth/nuxt\"\n          - \"@auth/qwik\"\n          - \"@auth/solid-start\"\n          - \"@auth/sveltekit\"\n          - \"@auth/azure-tables-adapter\"\n          - \"@auth/d1-adapter\"\n          - \"@auth/dgraph-adapter\"\n          - \"@auth/drizzle-adapter\"\n          - \"@auth/dynamodb-adapter\"\n          - \"@auth/edgedb-adapter\"\n          - \"@auth/fauna-adapter\"\n          - \"@auth/firebase-adapter\"\n          - \"@auth/hasura-adapter\"\n          - \"@auth/kysely-adapter\"\n          - \"@auth/mikro-orm-adapter\"\n          - \"@auth/mongodb-adapter\"\n          - \"@auth/neo4j-adapter\"\n          - \"@auth/pg-adapter\"\n          - \"@auth/pouchdb-adapter\"\n          - \"@auth/prisma-adapter\"\n          - \"@auth/sequelize-adapter\"\n          - \"@auth/supabase-adapter\"\n          - \"@auth/surrealdb-adapter\"\n          - \"@auth/test-adapter\"\n          - \"@auth/typeorm-adapter\"\n          - \"@auth/typeorm-legacy-adapter\"\n          - \"@auth/unstorage-adapter\"\n          - \"@auth/upstash-redis-adapter\"\n          - \"@auth/xata-adapter\"\npermissions:\n  id-token: write\n\nenv:\n  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n  TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n  FORCE_COLOR: true\n  NPM_CONFIG_PROVENANCE: true\n\njobs:\n  test:\n    name: Test\n    runs-on: ubuntu-22.04\n    steps:\n      - name: Init\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - name: Install pnpm\n        uses: pnpm/action-setup@v4\n      - name: Setup Node\n        uses: actions/setup-node@v4\n        with:\n          node-version-file: \".nvmrc\"\n          cache: \"pnpm\"\n      - name: Install dependencies\n        run: pnpm install\n      - name: Peek\n        run: pnpm peek\n        if: ${{ github.repository == 'nextauthjs/next-auth' && github.event_name == 'push' && github.ref == 'refs/heads/main' }}\n      - name: Get base commit SHA from main\n        id: get_base_sha\n        run: echo \"BASE_SHA=$(git merge-base origin/main HEAD)\" >> $GITHUB_ENV\n      - name: Check for changes under /packages\n        id: check-packages\n        run: |\n          if git diff --name-only ${{ env.BASE_SHA }}...HEAD | grep '^packages/'; then\n            echo \"PACKAGES_CHANGES=true\" >> $GITHUB_ENV\n          else\n            echo \"PACKAGES_CHANGES=false\" >> $GITHUB_ENV\n          fi\n      - name: Build\n        if: ${{ env.PACKAGES_CHANGES == 'true' || github.ref == 'refs/heads/main' }}\n        run: pnpm build\n      - name: Check formatting\n        run: pnpm format\n        timeout-minutes: 15\n      - name: Run unit tests\n        if: ${{ env.PACKAGES_CHANGES == 'true' || github.ref == 'refs/heads/main' }}\n        run: pnpm test\n      - name: Install Playwright\n        if: github.repository == 'nextauthjs/next-auth'\n        run: pnpm exec playwright install --with-deps chromium\n      - name: Run E2E tests (Nextjs-Docker)\n        continue-on-error: true\n        if: false\n        timeout-minutes: 15\n        run: cd apps/examples/nextjs-docker && pnpm test:docker\n      - name: Run E2E tests\n        continue-on-error: true # TODO: Make this less flakey\n        if: ${{ env.PACKAGES_CHANGES == 'true' || github.ref == 'refs/heads/main' }}\n        timeout-minutes: 15\n        env:\n          AUTH_SECRET: ${{ secrets.AUTH_SECRET }}\n          TEST_KEYCLOAK_USERNAME: ${{ secrets.TEST_KEYCLOAK_USERNAME }}\n          TEST_KEYCLOAK_PASSWORD: ${{ secrets.TEST_KEYCLOAK_PASSWORD }}\n          AUTH_KEYCLOAK_ID: ${{ secrets.AUTH_KEYCLOAK_ID }}\n          AUTH_KEYCLOAK_SECRET: ${{ secrets.AUTH_KEYCLOAK_SECRET }}\n          AUTH_KEYCLOAK_ISSUER: ${{ secrets.AUTH_KEYCLOAK_ISSUER }}\n          AUTH_TRUST_HOST: 1\n          DEBUG: \"pw:webserver\"\n        run: pnpm test:e2e\n      - uses: actions/upload-artifact@v4\n        name: Upload Playwright artifacts\n        with:\n          name: playwright-traces\n          path: \"**/packages/next-auth/test-results/*/trace.zip\"\n          retention-days: 7\n      - uses: codecov/codecov-action@v4\n        if: always()\n        name: Coverage\n        with:\n          token: ${{ secrets.CODECOV_TOKEN }}\n\n  release-branch:\n    name: Publish branch\n    timeout-minutes: 120\n    runs-on: ubuntu-latest\n    needs: test\n    if: ${{ github.event_name == 'push' }}\n    environment: Production\n    steps:\n      - name: Init\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n          # Please upvote https://github.com/orgs/community/discussions/13836\n          token: ${{ secrets.GH_PAT }}\n      - name: Install pnpm\n        uses: pnpm/action-setup@v4\n      - name: Setup Node\n        uses: actions/setup-node@v4\n        with:\n          node-version-file: \".nvmrc\"\n          cache: \"pnpm\"\n      - name: Install dependencies\n        run: pnpm install\n      - name: Publish to npm and GitHub\n        run: pnpm release\n        env:\n          # Please upvote https://github.com/orgs/community/discussions/13836\n          GITHUB_TOKEN: ${{ secrets.GH_PAT }}\n          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n  release-pr:\n    name: Publish PR\n    timeout-minutes: 120\n    runs-on: ubuntu-latest\n    needs: test\n    if: ${{ github.event_name == 'pull_request' }}\n    environment: Preview\n    steps:\n      - name: Init\n        uses: actions/checkout@v4\n      - name: Install pnpm\n        uses: pnpm/action-setup@v4\n      - name: Setup Node\n        uses: actions/setup-node@v4\n        with:\n          node-version-file: \".nvmrc\"\n          cache: \"pnpm\"\n      - name: Install dependencies\n        run: pnpm install\n      - name: Determine version\n        uses: ./.github/version-pr\n        id: determine-version\n        env:\n          PR_NUMBER: ${{ github.event.number }}\n      - name: Publish to npm\n        run: |\n          cd packages/core\n          echo \"//registry.npmjs.org/:_authToken=$NPM_TOKEN\" >> .npmrc\n          pnpm publish --no-git-checks --access public --tag experimental\n        env:\n          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n      - name: Comment version on PR\n        uses: NejcZdovc/comment-pr@v2\n        with:\n          message:\n            \"🎉 Experimental release [published 📦️ on npm](https://npmjs.com/package/@auth/core/v/${{ env.VERSION }})!\\n \\\n            ```sh\\npnpm add @auth/core@${{ env.VERSION }}\\n```\\n \\\n            ```sh\\nyarn add @auth/core@${{ env.VERSION }}\\n```\\n \\\n            ```sh\\nnpm i @auth/core@${{ env.VERSION }}\\n```\"\n        env:\n          VERSION: ${{ steps.determine-version.outputs.version }}\n          GITHUB_TOKEN: ${{ secrets.GH_PAT }}\n  release-manual:\n    name: Publish manually\n    runs-on: ubuntu-latest\n    if: ${{ github.event_name == 'workflow_dispatch' }}\n    steps:\n      - name: Init\n        uses: actions/checkout@v4\n      - name: Install pnpm\n        uses: pnpm/action-setup@v4\n      - name: Setup Node\n        uses: actions/setup-node@v4\n        with:\n          node-version-file: \".nvmrc\"\n          cache: \"pnpm\"\n      - name: Install dependencies\n        run: pnpm install\n      - name: Map package name to path\n        run: |\n          case \"${{ github.event.inputs.name }}\" in\n            *\"-adapter\")\n              adapter_name=$(echo \"${{ github.event.inputs.name }}\" | sed 's/@auth\\///' | sed 's/-adapter//')\n              echo \"PACKAGE_PATH=adapter-${adapter_name}\" >> $GITHUB_ENV\n              ;;\n            \"next-auth\")\n              echo \"PACKAGE_PATH=next-auth\" >> $GITHUB_ENV\n              ;;\n            \"@auth/core\")\n              echo \"PACKAGE_PATH=core\" >> $GITHUB_ENV\n              ;;\n            *)\n              framework_name=$(echo \"${{ github.event.inputs.name }}\" | sed 's/@auth\\///')\n              echo \"PACKAGE_PATH=frameworks-${framework_name}\" >> $GITHUB_ENV\n              ;;\n          esac\n      - name: Determine version\n        uses: ./.github/version-pr\n        id: determine-version\n        env:\n          PACKAGE_PATH: ${{ env.PACKAGE_PATH }}\n      - name: Publish to npm\n        run: |\n          pnpm build\n          cd packages/$PACKAGE_PATH\n          echo \"//registry.npmjs.org/:_authToken=$NPM_TOKEN\" >> .npmrc\n          pnpm publish --no-git-checks --access public --tag experimental\n          echo \"🎉 Experimental release published 📦️ on npm: https://npmjs.com/package/${{ github.event.inputs.name }}/v/${{ env.VERSION }}\"\n          echo \"Install via: pnpm add ${{ github.event.inputs.name }}@${{ env.VERSION }}\"\n        env:\n          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n          PACKAGE_PATH: ${{ env.PACKAGE_PATH }}\n          VERSION: ${{ steps.determine-version.outputs.version }}\n"
  },
  {
    "path": ".github/workflows/sync-examples.yml",
    "content": "name: Sync Example Repositories\non:\n  push:\n    branches:\n      - main\n  workflow_dispatch:\njobs:\n  sync:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@v3\n      - name: Run GitHub File Sync\n        uses: balazsorban44/repo-file-sync-action@master\n        with:\n          GH_PAT: ${{ secrets.GH_PAT }}\n          IS_FINE_GRAINED: true\n          SKIP_PR: true\n          ORIGINAL_MESSAGE: true\n"
  },
  {
    "path": ".github/workflows/triage.yml",
    "content": "name: Triage issue\non:\n  issues:\n    types: [labeled, opened]\n  issue_comment:\n    types: [created]\n\nenv:\n  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\npermissions:\n  issues: write\n\njobs:\n  triage:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Nissuer\n        uses: balazsorban44/nissuer@1.9.2\n        with:\n          label-area-prefix: \"\"\n          label-area-section: \"[Provider|Adapter] type(.*)### Environment\"\n          label-comments: '{ \"incomplete\": \".github/invalid-reproduction.md\", \"good first issue\": \".github/good-first-issue.md\", \"help needed\": \".github/help-needed.md\" }'\n          reproduction-link-section: \"### Reproduction URL(.*)### Describe the issue\"\n          reproduction-invalid-label: \"invalid reproduction\"\n          reproduction-issue-labels: \"bug,\"\n          reproduction-blocklist: \"github.com/nextauthjs.*\"\n"
  },
  {
    "path": ".gitignore",
    "content": "# Misc\n.DS_Store\n.eslintcache\n.env\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\npackages/*/.npmrc\n\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log\nui-debug.log\n.pnpm-debug.log\n.husky\ntmp\n\n# Dependencies\nnode_modules\n\n# Build dirs\n.next\nbuild\ndist\n\n# Generated files\n.cache-loader\npackages/next-auth/providers\n# copied from @auth/core\npackages/frameworks-*/**/providers\npackages/*/*.js\n!packages/*/typedoc.config.js\npackages/*/*.d.ts\npackages/*/*.d.ts.map\npackages/*/lib\npackages/**/generated\n.xata*\n\n# Qwik needs to use .mjs. REVIEW: Check back, can we just use .js?\npackages/*/*.mjs\n\n# Development app\napps/dev/src/css\napps/dev/prisma/migrations\napps/dev/typeorm\napps/dev/nextjs-2\n\n# VS\n/.vs/slnx.sqlite-journal\n/.vs/slnx.sqlite\n/.vs\n.vscode/generated*\n\n# Jetbrains\n.idea\n\n# GitHub Actions runner\n/actions-runner\n/_work\n\n# DB\ndev.db*\npackages/adapter-prisma/prisma/dev.db\npackages/adapter-prisma/prisma/migrations\ndb.sqlite\npackages/adapter-supabase/supabase/.branches\npackages/adapter-drizzle/.drizzle\n\n# Tests\ncoverage\ndynamodblocal-bin\nfirestore-debug.log\ntest.schema.gql\ntest-results\nplaywright-report\nblob-report\nplaywright/.cache\n\n\n# Turborepo\n.turbo\n\n# Docs\ndocs/.next\ndocs/manifest.mjs\n\n# Core\npackages/core/src/providers/provider-types.ts\npackages/core/lib\npackages/core/providers\npackages/core/src/lib/pages/styles.ts\ndocs/docs/reference/core\n\n# Next.js\npackages/next-auth/lib\npackages/next-auth/providers\n# copied from @auth/core\npackages/next-auth/src/providers\ndocs/docs/reference/nextjs\n\n# SvelteKit\npackages/frameworks-sveltekit/index.*\npackages/frameworks-sveltekit/client.*\npackages/frameworks-sveltekit/.svelte-kit\npackages/frameworks-sveltekit/package\npackages/frameworks-sveltekit/vite.config.js.timestamp-*\npackages/frameworks-sveltekit/vite.config.ts.timestamp-*\ndocs/docs/reference/sveltekit\n\n# SolidStart\ndocs/docs/reference/solidstart\n\n\n# Express\ndocs/docs/reference/express\n\n\n# Adapters\ndocs/docs/reference/adapter\n\n## Drizzle migration folder\n.drizzle\n"
  },
  {
    "path": ".nvmrc",
    "content": "22"
  },
  {
    "path": ".prettierignore",
    "content": ".prettierignore\n.cache-loader\n.DS_Store\n.pnpm-debug.log\n.turbo\n.vscode/generated*\n/_work\n/actions-runner\nnode_modules\npatches\npnpm-lock.yaml\n.github/actions/issue-validator/index.mjs\n*.d.ts\n*.d.ts.map\n**/*.sh\n\n.svelte-kit\n.next\n.nuxt\n\n# --------------- Docs ---------------\n\n.next\ndocs/pages/reference\nstatic\ndocs/manifest.mjs\n\n# --------------- Packages ---------------\n\ncoverage\ndist\npackages/**/*.cjs\npackages/**/*.js\n!packages/*/scripts/*.js\n\n# @auth/core\npackages/core/src/providers/provider-types.ts\npackages/core/src/lib/pages/styles.ts\n\n# @auth/sveltekit\npackages/frameworks-sveltekit/package\npackages/frameworks-sveltekit/vite.config.{js,ts}.timestamp-*\n\n# @auth/express\npackages/frameworks-express/providers\n\n# next-auth\npackages/next-auth/src/providers/provider-types.ts\npackages/next-auth/css/index.css\n\n# Adapters\n.branches\ndb.sqlite\ndev.db\ndynamodblocal-bin\nfirebase-debug.log\nfirestore-debug.log\nmigrations\ntest.schema.gql\n\n# --------------- Apps ---------------\n\n# Examples should have their own Prettier config since they are templates too\napps/example-sveltekit\n\n# Development app\napps/dev/prisma\napps/dev/migrations\napps/dev/typeorm\n"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\"esbenp.prettier-vscode\", \"bradlc.vscode-tailwindcss\"]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"typescript.tsdk\": \"node_modules/typescript/lib\",\n  \"openInGitHub.remote.branch\": \"main\",\n  \"typescript.preferences.importModuleSpecifierEnding\": \"js\",\n  \"editor.formatOnSave\": true,\n  \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n}\n"
  },
  {
    "path": ".vscode/snippets.code-snippets",
    "content": "{\n  \"oauth2-spec\": {\n    \"description\": \"Markdown link to OAuth 2 specification\",\n    \"scope\": \"typescript\",\n    \"prefix\": \"oauth2\",\n    \"body\": [\"[OAuth 2](https://datatracker.ietf.org/doc/html/rfc6749)\"],\n  },\n  \"oidc-spec\": {\n    \"description\": \"Markdown link to OpenID Connect specification\",\n    \"scope\": \"typescript\",\n    \"prefix\": \"oidc\",\n    \"body\": [\"[OIDC](https://openid.net/specs/openid-connect-core-1_0.html)\"],\n  },\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "ISC License\n\nCopyright (c) 2022-2024, Balázs Orbán\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\"><img width=\"96px\" src=\"https://authjs.dev/img/logo-sm.png\" /></a>\n  <h3 align=\"center\">Auth.js</h3>\n  <p align=\"center\">Authentication for the Web.</p>\n  <p align=\"center\">Open Source. Full Stack. Own Your Data.</p>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://x.com/authjs\" ><img src=\"https://shields.io/badge/Follow @authjs-000?logo=x&style=flat-square\" alt=\"X (formerly known Twitter)\" /></a>\n    <a href=\"https://github.com/nextauthjs/next-auth/releases\"><img src=\"https://img.shields.io/npm/v/next-auth/latest?style=flat-square&label=latest%20stable\" alt=\"NPM next-auth@latest release\" /></a> \n    <!-- TODO: Should count `@auth/core` when NextAuth.js v5 is released as stable. -->\n    <a href=\"https://www.npmtrends.com/next-auth\"><img src=\"https://img.shields.io/npm/dm/next-auth?style=flat-square&color=cyan\" alt=\"Downloads\" /></a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\"><img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square&color=orange\" alt=\"GitHub Stars\" /></a>\n    <!-- <a href=\"https://codecov.io/gh/nextauthjs/next-auth\" ><img alt=\"Codecov\" src=\"https://img.shields.io/codecov/c/github/nextauthjs/next-auth?token=o2KN5GrPsY&style=flat-square&logo=codecov\"></a> -->\n    <img src=\"https://shields.io/badge/TypeScript-3178C6?logo=TypeScript&logoColor=fff&style=flat-square\" alt=\"TypeScript\" />\n  </p>\n  <p align=\"center\">\n    Auth.js is a set of open-source packages that are built on standard Web APIs for authentication in modern applications with any framework on any platform in any JS runtime.\n  </p>\n</p>\n\n> Auth js is now part of [Better Auth](https://better-auth.com/blog/authjs-joins-better-auth). We recommend new projects to start with Better Auth unless there are some very specific feature gaps (most notably stateless session management without a database).\n\n## Features\n\n### Flexible and easy to use\n\n- Designed to work with any OAuth service, it supports 2.0+, OIDC\n- Built-in support for [many popular sign-in services](https://github.com/nextauthjs/next-auth/tree/main/packages/core/src/providers)\n- Email/Passwordless authentication\n- Passkeys/WebAuthn support\n- Bring Your Database - or none! - stateless authentication with any backend (Active Directory, LDAP, etc.)\n- Runtime-agnostic, runs anywhere! (Docker, Node.js, Serverless, etc.)\n\n### Own your data\n\nAuth.js can be used with or without a database.\n\n- An open-source solution that allows you to keep control of your data\n- Built-in support for [MySQL, MariaDB, Postgres, Microsoft SQL Server, MongoDB, SQLite, GraphQL, etc.](https://adapters.authjs.dev)\n- Works great with databases from popular hosting providers\n\n### Secure by default\n\n- Promotes the use of passwordless sign-in mechanisms\n- Designed to be secure by default and encourage best practices for safeguarding user data\n- Uses Cross-Site Request Forgery (CSRF) Tokens on POST routes (sign in, sign out)\n- Default cookie policy aims for the most restrictive policy appropriate for each cookie\n- When JSON Web Tokens are used, they are encrypted by default (JWE) with A256CBC-HS512\n- Features tab/window syncing and session polling to support short-lived sessions\n- Attempts to implement the latest guidance published by [Open Web Application Security Project](https://owasp.org)\n\nAdvanced configuration allows you to define your routines to handle controlling what accounts are allowed to sign in, for encoding and decoding JSON Web Tokens and to set custom cookie security policies and session properties, so you can control who can sign in and how often sessions have to be re-validated.\n\n### TypeScript\n\nAuth.js libraries are written with type safety in mind. [Check out the docs](https://authjs.dev/getting-started/typescript) for more information.\n\n## Security\n\nIf you think you have found a vulnerability (or are not sure) in Auth.js or any of the related packages (i.e. Adapters), we ask you to read our [Security Policy](https://authjs.dev/security) to reach out responsibly. Please do not open Pull Requests/Issues/Discussions before consulting with us.\n\n## Acknowledgments\n\n[Auth.js is made possible thanks to all of its contributors.](https://authjs.dev/contributors)\n\n<a href=\"https://github.com/nextauthjs/next-auth/graphs/contributors\">\n  <img width=\"500px\" src=\"https://contrib.rocks/image?repo=nextauthjs/next-auth\" />\n</a>\n<div>\n<a href=\"https://vercel.com?utm_source=nextauthjs&utm_campaign=oss\"></a>\n</div>\n\n## Contributing\n\nWe're open to all community contributions! If you'd like to contribute in any way, please first read\nour [Contributing Guide](https://github.com/nextauthjs/.github/blob/main/CONTRIBUTING.md).\n\n## License\n\nISC\n"
  },
  {
    "path": "apps/dev/express/.gitignore",
    "content": "# API keys and secrets\n.env\n\n# Dependency directory\nnode_modules\n\n# Editors\n.idea\n*.iml\n.vscode/settings.json\n\n# OS metadata\n.DS_Store\nThumbs.db\n\n# Ignore built ts files\ndist/**/*\n\n# Ignore built css files\n/public/css/output.css\n\n"
  },
  {
    "path": "apps/dev/express/.prettierignore",
    "content": "\n.DS_Store\nnode_modules\n/dist\n/.turbo\n/package\n.env\n.env.*\n!.env.example\n\n# Ignore files for PNPM, NPM and YARN\npnpm-lock.yaml\npackage-lock.json\nyarn.lock\n"
  },
  {
    "path": "apps/dev/express/README.md",
    "content": "> The example repository is maintained from a [monorepo](https://github.com/nextauthjs/next-auth/tree/main/apps/examples/express). Pull Requests should be opened against [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth).\n\n<p align=\"center\">\n   <br/>\n   <a href=\"https://authjs.dev\" target=\"_blank\"><img width=\"150px\" src=\"https://authjs.dev/img/logo-sm.png\" /></a>\n   <h3 align=\"center\">Auth.js Example App with <a href=\"https://expressjs.com\">Express</a></h3>\n   <p align=\"center\">\n   Open Source. Full Stack. Own Your Data.\n   </p>\n   <p align=\"center\" style=\"align: center;\">\n      <a href=\"https://npm.im/@auth/express\">\n        <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/express?color=green&label=@auth/express&style=flat-square\">\n      </a>\n      <a href=\"https://bundlephobia.com/result?p=@auth/express\">\n        <img src=\"https://img.shields.io/bundlephobia/minzip/@auth/express?label=size&style=flat-square\" alt=\"Bundle Size\"/>\n      </a>\n      <a href=\"https://www.npmtrends.com/@auth/express\">\n        <img src=\"https://img.shields.io/npm/dm/@auth/express?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n      </a>\n      <a href=\"https://npm.im/next-auth\">\n        <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n      </a>\n   </p>\n</p>\n\n# Documentation\n\n- [express.authjs.dev](https://express.authjs.dev)\n"
  },
  {
    "path": "apps/dev/express/api/index.js",
    "content": "import { app } from \"../src/app.js\"\n\nexport default app\n"
  },
  {
    "path": "apps/dev/express/package.json",
    "content": "{\n  \"name\": \"express-auth-app\",\n  \"description\": \"Express + Auth.js Developer app\",\n  \"type\": \"module\",\n  \"private\": true,\n  \"scripts\": {\n    \"start\": \"node --env-file=.env dist/server.js\",\n    \"clean\": \"rm -rf dist\",\n    \"build\": \"pnpm build:ts && pnpm build:css\",\n    \"build:ts\": \"tsc\",\n    \"build:css\": \"tailwindcss -i ./public/css/style.css -o ./public/css/output.css\",\n    \"dev\": \"tsx watch --env-file=.env src/server.ts & pnpm build:css -w\",\n    \"lint\": \"eslint src/*.ts --fix\",\n    \"prettier\": \"prettier src/*.ts --write\"\n  },\n  \"author\": \"Auth.js Team (https://authjs.dev/contributors)\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"@auth/express\": \"workspace:*\",\n    \"express\": \"^4.20.0\",\n    \"morgan\": \"^1.10.0\",\n    \"pug\": \"^3.0.2\"\n  },\n  \"devDependencies\": {\n    \"@prettier/plugin-pug\": \"^3.0.0\",\n    \"@types/express\": \"^4.17.21\",\n    \"@types/morgan\": \"^1.9.9\",\n    \"@types/pug\": \"^2.0.10\",\n    \"tsx\": \"^4.7.3\",\n    \"typescript\": \"5.4.5\"\n  }\n}\n"
  },
  {
    "path": "apps/dev/express/public/css/style.css",
    "content": "@tailwind base;\n\n@tailwind components;\n\n@tailwind utilities;\n"
  },
  {
    "path": "apps/dev/express/src/app.ts",
    "content": "import express, { type Request, type Response } from \"express\"\nimport logger from \"morgan\"\nimport { join } from \"node:path\"\n\nimport {\n  errorHandler,\n  errorNotFoundHandler,\n} from \"./middleware/error.middleware.js\"\n\nimport {\n  authenticatedUser,\n  currentSession,\n} from \"./middleware/auth.middleware.js\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { authConfig } from \"./config/auth.config.js\"\nimport * as pug from \"pug\"\n\nexport const app = express()\n\napp.set(\"port\", process.env.PORT || 3004)\n\n// @ts-expect-error (https://stackoverflow.com/questions/45342307/error-cannot-find-module-pug)\napp.engine(\"pug\", pug.__express)\napp.set(\"views\", join(import.meta.dirname, \"..\", \"views\"))\napp.set(\"view engine\", \"pug\")\n\n// Trust Proxy for Proxies (Heroku, Render.com, Docker behind Nginx, etc)\n// https://stackoverflow.com/questions/40459511/in-express-js-req-protocol-is-not-picking-up-https-for-my-secure-link-it-alwa\napp.set(\"trust proxy\", true)\n\napp.use(logger(\"dev\"))\n\n// Serve static files\n// NB: Uncomment this out if you want Express to serve static files for you vs. using a\n// hosting provider which does so for you (for example through a CDN).\n// app.use(express.static(join(import.meta.dirname, \"..\", \"public\")))\n\n// Parse incoming requests data\napp.use(express.urlencoded({ extended: true }))\napp.use(express.json())\n\n// Set session in res.locals\napp.use(currentSession)\n\n// Set up ExpressAuth to handle authentication\n// IMPORTANT: It is highly encouraged set up rate limiting on this route\napp.use(\"/api/auth/*\", ExpressAuth(authConfig))\n\n// Routes\napp.get(\"/protected\", async (_req: Request, res: Response) => {\n  res.render(\"protected\", { session: res.locals.session })\n})\n\napp.get(\n  \"/api/protected\",\n  authenticatedUser,\n  async (_req: Request, res: Response) => {\n    res.json(res.locals.session)\n  }\n)\n\napp.get(\"/\", async (_req: Request, res: Response) => {\n  res.render(\"index\", {\n    title: \"Express Auth Example\",\n    user: res.locals.session?.user,\n  })\n})\n\n// Error handlers\napp.use(errorNotFoundHandler)\napp.use(errorHandler)\n"
  },
  {
    "path": "apps/dev/express/src/config/auth.config.ts",
    "content": "import Apple from \"@auth/express/providers/apple\"\nimport Auth0 from \"@auth/express/providers/auth0\"\nimport AzureB2C from \"@auth/express/providers/azure-ad-b2c\"\nimport BoxyHQSAML from \"@auth/express/providers/boxyhq-saml\"\nimport Cognito from \"@auth/express/providers/cognito\"\nimport Coinbase from \"@auth/express/providers/coinbase\"\nimport Discord from \"@auth/express/providers/discord\"\nimport Dropbox from \"@auth/express/providers/dropbox\"\nimport Facebook from \"@auth/express/providers/facebook\"\nimport GitHub from \"@auth/express/providers/github\"\nimport Gitlab from \"@auth/express/providers/gitlab\"\nimport Google from \"@auth/express/providers/google\"\nimport Hubspot from \"@auth/express/providers/hubspot\"\nimport Keycloak from \"@auth/express/providers/keycloak\"\nimport LinkedIn from \"@auth/express/providers/linkedin\"\nimport Netlify from \"@auth/express/providers/netlify\"\nimport Okta from \"@auth/express/providers/okta\"\nimport Passage from \"@auth/express/providers/passage\"\nimport Pinterest from \"@auth/express/providers/pinterest\"\nimport Reddit from \"@auth/express/providers/reddit\"\nimport Slack from \"@auth/express/providers/slack\"\nimport Spotify from \"@auth/express/providers/spotify\"\nimport Twitch from \"@auth/express/providers/twitch\"\nimport Twitter from \"@auth/express/providers/twitter\"\nimport WorkOS from \"@auth/express/providers/workos\"\nimport Zoom from \"@auth/express/providers/zoom\"\n\nexport const authConfig = {\n  trustHost: true,\n  debug: process.env.NODE_ENV !== \"production\" ? true : false,\n  providers: [\n    Apple,\n    Auth0,\n    AzureB2C({\n      clientId: process.env.AUTH_AZURE_AD_B2C_ID,\n      clientSecret: process.env.AUTH_AZURE_AD_B2C_SECRET,\n      issuer: process.env.AUTH_AZURE_AD_B2C_ISSUER,\n    }),\n    BoxyHQSAML({\n      clientId: \"dummy\",\n      clientSecret: \"dummy\",\n      issuer: process.env.AUTH_BOXYHQ_SAML_ISSUER,\n    }),\n    Cognito,\n    Coinbase,\n    Discord,\n    Dropbox,\n    Facebook,\n    GitHub,\n    Gitlab,\n    Google,\n    Hubspot,\n    Keycloak,\n    LinkedIn,\n    Netlify,\n    Okta,\n    Passage,\n    Pinterest,\n    Reddit,\n    Slack,\n    Spotify,\n    Twitch,\n    Twitter,\n    WorkOS({\n      connection: process.env.AUTH_WORKOS_CONNECTION!,\n    }),\n    Zoom,\n  ],\n}\n"
  },
  {
    "path": "apps/dev/express/src/errors.ts",
    "content": "export class HttpError extends Error {\n  status: number\n  constructor(status: number, message: string) {\n    super(message)\n    this.status = status\n  }\n}\n\nexport class NotFoundError extends HttpError {\n  constructor(message: string, status = 404) {\n    super(status, message)\n    this.name = \"NotFoundError\"\n  }\n}\n"
  },
  {
    "path": "apps/dev/express/src/middleware/auth.middleware.ts",
    "content": "import { getSession } from \"@auth/express\"\nimport { authConfig } from \"../config/auth.config.js\"\nimport type { NextFunction, Request, Response } from \"express\"\n\nexport async function authenticatedUser(\n  req: Request,\n  res: Response,\n  next: NextFunction\n) {\n  const session = res.locals.session ?? (await getSession(req, authConfig))\n\n  res.locals.session = session\n\n  if (session) {\n    return next()\n  }\n\n  res.status(400).json({ message: \"Not Authenticated\" })\n}\n\nexport async function currentSession(\n  req: Request,\n  res: Response,\n  next: NextFunction\n) {\n  const session = await getSession(req, authConfig)\n  res.locals.session = session\n  return next()\n}\n"
  },
  {
    "path": "apps/dev/express/src/middleware/error.middleware.ts",
    "content": "import type { NextFunction, Request, Response } from \"express\"\nimport { HttpError, NotFoundError } from \"../errors.js\"\n\nexport const errorHandler = (\n  err: HttpError | Error,\n  _req: Request,\n  res: Response,\n  _next: NextFunction\n): void => {\n  // Render the error page\n  res.status((\"status\" in err && err.status) || 500)\n  res.render(\"error\", {\n    title: \"status\" in err ? err.status : err.name,\n    message: err.message,\n  })\n}\n\nexport const errorNotFoundHandler = (\n  _req: Request,\n  _res: Response,\n  next: NextFunction\n): void => {\n  next(new NotFoundError(\"Not Found\"))\n}\n"
  },
  {
    "path": "apps/dev/express/src/server.ts",
    "content": "import { app } from \"./app.js\"\n\nconst port = app.get(\"port\")\n\nconst server = app.listen(port, () => {\n  console.log(`Listening on port ${port}`)\n})\n\nexport default server\n"
  },
  {
    "path": "apps/dev/express/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"NodeNext\",\n    \"esModuleInterop\": true,\n    \"target\": \"esnext\",\n    \"noImplicitAny\": true,\n    \"moduleResolution\": \"NodeNext\",\n    \"sourceMap\": true,\n    \"outDir\": \"dist\",\n    \"baseUrl\": \".\",\n    \"skipLibCheck\": true,\n    \"strict\": true\n  },\n  \"include\": [\"src/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "apps/dev/express/views/error.pug",
    "content": "extends layout\n\nblock content\n  h1=title\n  p=message\n"
  },
  {
    "path": "apps/dev/express/views/index.pug",
    "content": "extends layout\n\nblock content\n  h1=title\n  p\n    | This is an example site to demonstrate how to use #{ ' ' }\n    a(href=\"https://expressjs.com/\") Express\n    | #{ ' ' } with #{ ' ' }\n    a(href=\"https://authjs.dev/reference/express\") Express Auth\n    |\n    | for authentication.\n"
  },
  {
    "path": "apps/dev/express/views/layout.pug",
    "content": "doctype html\nhtml\n  head\n    title=title\n    meta(name=\"viewport\" content=\"width=device-width, initial-scale=1.0\")\n  body\n    div\n      div\n        if session\n          div\n            if session.user.image\n              img(src=`${session.user.image}` style=\"width:64px;border-radius:50%;\")\n            span\n              | Signed in as #{ ' ' }\n              strong= session.user.email || session.user.name\n          a(\n            href=\"/api/auth/signout\"\n          ) Sign out\n        else\n          span You are not signed in #{ ' ' }\n          a#sign-indiv(\n            href=\"/api/auth/signin\"\n          ) Sign in\n\n      nav\n        ul\n          li\n            a(href=\"/\") Home\n          li\n            a(href=\"/protected\") Protected\n          li\n            a(href=\"/api/protected\") Protected (API)\n\n      block content\n"
  },
  {
    "path": "apps/dev/express/views/protected.pug",
    "content": "extends layout\n\nblock content\n  if session\n    h1 Protected page\n    p\n      | This is a protected content. You can access this content because you are\n      | signed in.\n    p Session expiry: #{ session.expires ? session.expires : '' }\n  else\n    h1 Access Denied\n    p\n      | You must be #{ ' ' }\n      a(href=\"/api/auth/signin\") signed in\n      | #{ ' ' } to view this page\n"
  },
  {
    "path": "apps/dev/nextjs/.gitignore",
    "content": "node_modules/\n/test-results/\n/playwright-report/\n/playwright/.cache/\ndbschema/edgeql-js\n"
  },
  {
    "path": "apps/dev/nextjs/.vscode/settings.json",
    "content": "{\n  \"typescript.tsdk\": \"../../../node_modules/.pnpm/typescript@4.9.4/node_modules/typescript/lib\",\n  \"typescript.enablePromptUseWorkspaceTsdk\": true\n}\n"
  },
  {
    "path": "apps/dev/nextjs/README.md",
    "content": "# NextAuth.js Development App\n\nThis folder contains a Next.js app using NextAuth.js for local development. See the following section on how to start:\n\n[Setting up local environment\n](https://github.com/nextauthjs/.github/blob/main/CONTRIBUTING.md#setting-up-local-environment)\n"
  },
  {
    "path": "apps/dev/nextjs/app/api/protected/route.ts",
    "content": "import { auth } from \"auth\"\nimport { NextResponse } from \"next/server\"\n\nexport const GET = auth(function GET(req) {\n  if (req.auth) return NextResponse.json(req.auth)\n  return NextResponse.json({ message: \"Not authenticated\" }, { status: 401 })\n})\n"
  },
  {
    "path": "apps/dev/nextjs/app/auth/[...nextauth]/route.ts",
    "content": "import { handlers } from \"auth\"\nexport const { GET, POST } = handlers\n\n// export const runtime = \"edge\"\n"
  },
  {
    "path": "apps/dev/nextjs/app/client.tsx",
    "content": "\"use client\"\n\nimport { signIn, signOut, useSession } from \"next-auth/react\"\nimport { useRouter } from \"next/navigation\"\n\nexport default function Client() {\n  const { data: session, update, status } = useSession()\n  const router = useRouter()\n  return (\n    <div className=\"card\">\n      <div className=\"card-header\">\n        <h3>Client Component</h3>\n      </div>\n      <div className=\"card-body\">\n        <h4>Session</h4>\n        <pre>\n          {status === \"loading\"\n            ? \"Loading...\"\n            : JSON.stringify(session, null, 2)}\n        </pre>\n        <div className=\"btn-wrapper\">\n          {session ? (\n            <>\n              <button\n                onClick={async () => {\n                  await update({ user: { name: \"Client Fill Murray\" } })\n                  router.refresh()\n                }}\n              >\n                Update Session - New Name\n              </button>\n              <button onClick={() => signOut()}>Sign out</button>\n            </>\n          ) : (\n            <>\n              <button onClick={() => signIn(\"github\")}>Sign in GitHub</button>\n              <button\n                onClick={async () => {\n                  await signIn(\"webauthn\", {})\n                }}\n              >\n                Sign in Credentials\n              </button>\n            </>\n          )}\n        </div>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/dev/nextjs/app/dashboard/page.tsx",
    "content": "export default function Page() {\n  return <h1>This page is protected.</h1>\n}\n"
  },
  {
    "path": "apps/dev/nextjs/app/layout.tsx",
    "content": "import { auth, signIn, signOut, unstable_update as update } from \"auth\"\nimport Footer from \"components/footer\"\nimport { Header } from \"components/header\"\nimport styles from \"components/header.module.css\"\nimport \"./styles.css\"\nimport { AuthError } from \"next-auth\"\n\nexport default function RootLayout(props: { children: React.ReactNode }) {\n  return (\n    <html>\n      <body>\n        <AppHeader />\n        <main>{props.children}</main>\n        <Footer />\n      </body>\n    </html>\n  )\n}\n\nexport async function AppHeader() {\n  const session = await auth()\n  return (\n    <Header\n      session={session}\n      signIn={\n        <form\n          action={async () => {\n            \"use server\"\n            try {\n              await signIn()\n            } catch (error) {\n              if (error instanceof AuthError) {\n                console.log(error)\n              }\n              throw error\n            }\n          }}\n        >\n          <button className={styles.buttonPrimary}>Sign in</button>\n        </form>\n      }\n      signOut={\n        <form\n          action={async () => {\n            \"use server\"\n            await signOut()\n          }}\n        >\n          <button className={styles.buttonPrimary}>Sign out</button>\n        </form>\n      }\n    />\n  )\n}\n"
  },
  {
    "path": "apps/dev/nextjs/app/page.tsx",
    "content": "import { auth, unstable_update as update } from \"auth\"\nimport { SessionProvider } from \"next-auth/react\"\nimport Client from \"./client\"\nimport { revalidatePath } from \"next/cache\"\nimport { redirect } from \"next/navigation\"\n\nexport default async function Page() {\n  const session = await auth()\n  return (\n    <div className=\"container\">\n      <h1>NextAuth.js Example</h1>\n      <p>\n        This is an example site to demonstrate how to use{\" \"}\n        <a href=\"https://nextjs.authjs.dev\">NextAuth.js</a> for authentication.\n      </p>\n      <div className=\"card\">\n        <div className=\"card-header\">\n          <h3>Server Action</h3>\n        </div>\n        <div className=\"card-body\">\n          {session ? (\n            <form\n              action={async () => {\n                \"use server\"\n                await update({ user: { name: \"Server Fill Murray\" } })\n                // revalidatePath(\"/\")\n                redirect(\"/\")\n              }}\n            >\n              <button>Update Session - New Name</button>\n            </form>\n          ) : null}\n        </div>\n        <div className=\"card-footer\">\n          Note: The \"Sign in\" button in the header is using{\" \"}\n          <b>server form actions</b>.\n        </div>\n      </div>\n      {/* \n       NOTE: The `auth()` result is not run through the `session` callback, be careful passing down data\n       to a client component, this will be exposed via the /api/auth/session endpoint\n      */}\n      <SessionProvider session={session} basePath=\"/auth\">\n        <Client />\n      </SessionProvider>\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/dev/nextjs/app/styles.css",
    "content": "body {\n  font-family:\n    ui-sans-serif,\n    system-ui,\n    -apple-system,\n    BlinkMacSystemFont,\n    \"Segoe UI\",\n    Roboto,\n    \"Helvetica Neue\",\n    Arial,\n    \"Noto Sans\",\n    sans-serif,\n    \"Apple Color Emoji\",\n    \"Segoe UI Emoji\",\n    \"Segoe UI Symbol\",\n    \"Noto Color Emoji\";\n  max-width: 960px;\n  margin: 0 auto;\n  background: #fff;\n  color: var(--color-text);\n  height: 100dvh;\n  display: flex;\n  flex-direction: column;\n}\n\nli,\np {\n  line-height: 1.5rem;\n  margin: 0;\n}\n\na {\n  font-weight: 500;\n}\n\nhr {\n  border: 1px solid #ddd;\n}\n\niframe {\n  background: #ccc;\n  border: 1px solid #ccc;\n  height: 10rem;\n  width: 100%;\n  border-radius: 0.5rem;\n  filter: invert(1);\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  margin: 0;\n}\n\nmain {\n  flex-grow: 1;\n\n  @media screen and (max-width: 960px) {\n    padding: 0 1rem;\n  }\n}\n\n.container {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  align-items: stretch;\n  justify-content: flex-start;\n}\n\n.card {\n  border-radius: 0.75rem;\n  background-color: #f3f3f3;\n\n  .card-header {\n    border-radius: 0.75rem 0.75rem 0 0;\n    background-color: #dfdfdf;\n    padding: 1.1rem;\n  }\n\n  .card-body {\n    padding-bottom: 1.1rem;\n\n    &:has(button) {\n      padding: 1.1rem;\n    }\n  }\n\n  .card-footer {\n    padding: 1.1rem;\n    padding-top: 0;\n    color: #777;\n    font-style: italic;\n  }\n\n  pre {\n    background-color: #ccc;\n    padding: 1rem;\n    border-radius: 0.5rem;\n    word-break: break-all;\n    white-space: pre-wrap;\n  }\n\n  .btn-wrapper {\n    display: flex;\n    gap: 1rem;\n  }\n\n  button {\n    justify-self: end;\n    font-weight: 500;\n    border-radius: 0.5rem;\n    border: none;\n    font-weight: bold;\n    cursor: pointer;\n    font-size: 1rem;\n    line-height: 1.4rem;\n    padding: 0.7rem 0.8rem;\n    position: relative;\n    z-index: 10;\n    background-color: #d5d5d5;\n    color: black;\n    text-decoration: none;\n    padding: 0.7rem 1.4rem;\n  }\n  button:hover {\n    background-color: #ccc;\n  }\n}\n"
  },
  {
    "path": "apps/dev/nextjs/auth.ts",
    "content": "import NextAuth from \"next-auth\"\nimport Credentials from \"next-auth/providers/credentials\"\nimport Keycloak from \"next-auth/providers/keycloak\"\nimport GitHub from \"next-auth/providers/github\"\n\n// import { PrismaClient } from \"@prisma/client\"\n// import { PrismaAdapter } from \"@auth/prisma-adapter\"\n// import SendGrid from \"next-auth/providers/sendgrid\"\n// import Resend from \"next-auth/providers/resend\"\n// import Email from \"next-auth/providers/email\"\n\n// globalThis.prisma ??= new PrismaClient()\n\n// authConfig.providers.push(\n//   // Start server with `pnpm email`\n//   Email({ server: \"smtp://127.0.0.1:1025?tls.rejectUnauthorized=false\" }),\n//   SendGrid,\n//   Resend\n// )\n\n// export const { handlers, auth, signIn, signOut, unstable_update } = NextAuth(\n//   (request) => {\n//     if (request?.nextUrl.searchParams.get(\"test\")) {\n//       return {\n//         // adapter: PrismaAdapter(globalThis.prisma),\n//         session: { strategy: \"jwt\" },\n//         ...authConfig,\n//         providers: [],\n//       }\n//     }\n//     return {\n//       // adapter: PrismaAdapter(globalThis.prisma),\n//       session: { strategy: \"jwt\" },\n//       ...authConfig,\n//     }\n//   }\n// )\n\ndeclare module \"next-auth\" {\n  /**\n   * Returned by `useSession`, `getSession`, `auth` and received as a prop on the `SessionProvider` React Context\n   */\n  interface Session {\n    user: {\n      /** The user's postal address. */\n      address: string\n    } & User\n  }\n\n  interface User {\n    foo?: string\n  }\n}\n\nexport const { handlers, auth, signIn, signOut, unstable_update } = NextAuth({\n  debug: true,\n  providers: [\n    Credentials({\n      credentials: { password: { label: \"Password\", type: \"password\" } },\n      authorize(c) {\n        if (c.password !== \"password\") return null\n        return {\n          id: \"test\",\n          name: \"Test User\",\n          email: \"test@example.com\",\n        }\n      },\n    }),\n    GitHub,\n    Keycloak,\n  ],\n\n  callbacks: {\n    jwt({ token, trigger, session }) {\n      if (trigger === \"update\") token.name = session.user.name\n      return token\n    },\n  },\n  basePath: \"/auth\",\n  session: { strategy: \"jwt\" },\n})\n"
  },
  {
    "path": "apps/dev/nextjs/components/access-denied.tsx",
    "content": "import { signIn } from \"next-auth/react\"\n\nexport default function AccessDenied() {\n  return (\n    <>\n      <h1>Access Denied</h1>\n      <p>\n        <a\n          href=\"/api/auth/signin\"\n          onClick={(e) => {\n            e.preventDefault()\n            signIn()\n          }}\n        >\n          You must be signed in to view this page\n        </a>\n      </p>\n    </>\n  )\n}\n"
  },
  {
    "path": "apps/dev/nextjs/components/footer.module.css",
    "content": ".footer {\n  margin: 0;\n  padding: 0 1rem 0 1rem;\n\n  @media screen and (min-width: 960px) {\n    padding: 0;\n  }\n}\n\n.navItems {\n  padding: 2rem 0rem 2rem 0rem;\n  margin: 0;\n  list-style: none;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.navItemsLeft {\n  display: flex;\n  align-items: center;\n\n  @media screen and (max-width: 460px) {\n    flex-direction: column;\n    align-items: flex-start;\n  }\n}\n\n.navItem {\n  display: inline-block;\n  margin-right: 1rem;\n  display: flex;\n  align-items: center;\n}\n\n.navItem a {\n  color: rgb(2, 8, 23);\n  text-underline-offset: 4px;\n  font-size: 14px;\n}\n\n.navItem svg {\n  margin-left: 4px;\n  height: 16px;\n  width: 16px;\n}\n\n.footerLogo {\n  height: 20px;\n  width: 20px;\n  margin-right: 0.5rem;\n}\n"
  },
  {
    "path": "apps/dev/nextjs/components/footer.tsx",
    "content": "import Link from \"next/link\"\nimport styles from \"./footer.module.css\"\nimport packageJSON from \"next-auth/package.json\"\n\nexport default function Footer() {\n  return (\n    <footer className={styles.footer}>\n      <ul className={styles.navItems}>\n        <div className={styles.navItemsLeft}>\n          <li className={styles.navItem}>\n            <a href=\"https://authjs.dev\">Documentation</a>\n            <svg\n              xmlns=\"http://www.w3.org/2000/svg\"\n              width=\"16\"\n              height=\"16\"\n              viewBox=\"0 0 24 24\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              strokeWidth=\"2\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n            >\n              <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"></path>\n              <polyline points=\"15 3 21 3 21 9\"></polyline>\n              <line x1=\"10\" x2=\"21\" y1=\"14\" y2=\"3\"></line>\n            </svg>\n          </li>\n          <li className={styles.navItem}>\n            <a href=\"https://www.npmjs.com/package/next-auth\">NPM</a>\n            <svg\n              xmlns=\"http://www.w3.org/2000/svg\"\n              width=\"16\"\n              height=\"16\"\n              viewBox=\"0 0 24 24\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              strokeWidth=\"2\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n            >\n              <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"></path>\n              <polyline points=\"15 3 21 3 21 9\"></polyline>\n              <line x1=\"10\" x2=\"21\" y1=\"14\" y2=\"3\"></line>\n            </svg>\n          </li>\n          <li className={styles.navItem}>\n            <a href=\"https://github.com/nextauthjs/next-auth-example\">GitHub</a>\n            <svg\n              xmlns=\"http://www.w3.org/2000/svg\"\n              width=\"16\"\n              height=\"16\"\n              viewBox=\"0 0 24 24\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              strokeWidth=\"2\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n            >\n              <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"></path>\n              <polyline points=\"15 3 21 3 21 9\"></polyline>\n              <line x1=\"10\" x2=\"21\" y1=\"14\" y2=\"3\"></line>\n            </svg>\n          </li>\n          <li className={styles.navItem}>\n            <Link href=\"/policy\">Policy</Link>\n          </li>\n        </div>\n        <li className={styles.navItem} style={{ margin: \"0\" }}>\n          <img\n            className={styles.footerLogo}\n            src=\"https://authjs.dev/img/logo-sm.png\"\n            alt=\"Auth.js Logo\"\n          />\n          <Link href=\"https://npmjs.org/package/next-auth\">\n            {packageJSON.version}\n          </Link>\n          <svg\n            xmlns=\"http://www.w3.org/2000/svg\"\n            width=\"16\"\n            height=\"16\"\n            viewBox=\"0 0 24 24\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            strokeWidth=\"2\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n          >\n            <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"></path>\n            <polyline points=\"15 3 21 3 21 9\"></polyline>\n            <line x1=\"10\" x2=\"21\" y1=\"14\" y2=\"3\"></line>\n          </svg>\n        </li>\n      </ul>\n    </footer>\n  )\n}\n"
  },
  {
    "path": "apps/dev/nextjs/components/header.module.css",
    "content": "/* Set min-height to avoid page reflow while session loading */\n.signedInStatus {\n  display: flex;\n  align-items: center;\n  min-height: 4rem;\n  padding: 1rem;\n  background-color: #f3f3f3;\n}\n\n.loading,\n.loaded {\n  position: relative;\n  top: 0;\n  opacity: 1;\n  overflow: hidden;\n  border-radius: 0 0 0.6rem 0.6rem;\n  padding: 0.6rem 1rem;\n  margin: 0;\n  background-color: rgba(0, 0, 0, 0.05);\n  transition: all 0.2s ease-in;\n}\n\n.loading {\n  top: -2rem;\n  opacity: 0;\n}\n\n.signedInText,\n.notSignedInText {\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  overflow: hidden;\n  z-index: 1;\n  line-height: 1.3rem;\n  flex: 1;\n}\n\n.signedInText {\n  padding-top: 0rem;\n  left: 4.6rem;\n}\n\n.avatar {\n  border-radius: 2rem;\n  float: left;\n  height: 2.8rem;\n  width: 2.8rem;\n  margin-right: 1rem;\n  background-color: white;\n  background-size: cover;\n  background-repeat: no-repeat;\n}\n\n.button,\n.buttonPrimary {\n  justify-self: end;\n  font-weight: 500;\n  border-radius: 0.3rem;\n  border: none;\n  font-weight: bold;\n  cursor: pointer;\n  font-size: 1rem;\n  line-height: 1.4rem;\n  padding: 0.7rem 0.8rem;\n  position: relative;\n  z-index: 10;\n  background-color: transparent;\n  color: #555;\n}\n\n.buttonPrimary {\n  background-color: #346df1;\n  border-color: #346df1;\n  color: #fff;\n  text-decoration: none;\n  padding: 0.7rem 1.4rem;\n}\n\n.buttonPrimary:hover {\n  box-shadow: inset 0 0 5rem rgba(0, 0, 0, 0.2);\n}\n\n.navItems {\n  padding: 0;\n  margin: 0;\n  list-style: none;\n  display: flex;\n  justify-content: space-between;\n  flex-wrap: wrap;\n}\n\n.navItem {\n  flex-grow: 1;\n  display: inline-block;\n  text-align: center;\n  background-color: #d5d5d5;\n  border-radius: 0 0 0.5rem 0.5rem;\n  padding: 1rem 0 1rem 0;\n  text-decoration: none;\n  color: black;\n}\n\n.navItem:hover {\n  background-color: #ccc;\n}\n\n.header {\n  border-radius: 0 0 0.6rem 0.6rem;\n  margin-bottom: 2rem;\n\n  @media screen and (max-width: 960px) {\n    padding: 0 1rem;\n  }\n}\n\n.passwordInput {\n  padding: 0.75rem;\n  border-radius: 0.3rem;\n  border: #ccc solid 2px;\n  margin-right: 1rem;\n}\n"
  },
  {
    "path": "apps/dev/nextjs/components/header.tsx",
    "content": "import type { Session } from \"next-auth\"\nimport Link from \"next/link\"\nimport styles from \"./header.module.css\"\n\nexport function Header({\n  session,\n  signIn,\n  signOut,\n}: {\n  session: Session | null\n  signIn: any\n  signOut: any\n}) {\n  return (\n    <header className={styles.header}>\n      <div className={styles.signedInStatus}>\n        <img\n          src={\n            session?.user?.image ??\n            `https://api.dicebear.com/9.x/thumbs/svg?seed=${Math.floor(Math.random() * 100000) + 1}&randomizeIds=true`\n          }\n          className={styles.avatar}\n        />\n        {session?.user ? (\n          <>\n            <span className={styles.signedInText}>\n              <small>Signed in as</small>\n              <br />\n              <strong>{session.user?.email} </strong>\n              {session.user?.name ? `(${session.user.name})` : null}\n            </span>\n            {signOut}\n          </>\n        ) : (\n          <>\n            <span className={styles.notSignedInText}>\n              You are not signed in\n            </span>\n            {signIn}\n          </>\n        )}\n      </div>\n      <nav>\n        <ul className={styles.navItems}>\n          <Link href=\"/\" className={styles.navItem}>\n            Home (app)\n          </Link>\n          <Link className={styles.navItem} href=\"/dashboard\">\n            Dashboard (app)\n          </Link>\n          <Link className={styles.navItem} href=\"/policy\">\n            Policy (pages)\n          </Link>\n          <Link className={styles.navItem} href=\"/credentials\">\n            Credentials (pages)\n          </Link>\n          <Link className={styles.navItem} href=\"/protected-ssr\">\n            getServerSideProps (pages)\n          </Link>\n          <Link className={styles.navItem} href=\"/api/examples/protected\">\n            API Route (pages)\n          </Link>\n        </ul>\n      </nav>\n    </header>\n  )\n}\n"
  },
  {
    "path": "apps/dev/nextjs/middleware.ts",
    "content": "export { auth as middleware } from \"auth\"\n\n// Or like this if you need to do something here.\n// export default auth((req) => {\n//   console.log(req.auth) //  { session: { user: { ... } } }\n// })\n\n// Read more: https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher\nexport const config = {\n  matcher: [\"/((?!api|_next/static|_next/image|favicon.ico).*)\"],\n}\n"
  },
  {
    "path": "apps/dev/nextjs/next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n/// <reference types=\"next/navigation-types/compat/navigation\" />\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.\n"
  },
  {
    "path": "apps/dev/nextjs/next.config.js",
    "content": "/** @type {import(\"next\").NextConfig} */\nmodule.exports = {\n  webpack(config) {\n    config.experiments = { ...config.experiments, topLevelAwait: true }\n    return config\n  },\n  typescript: { ignoreBuildErrors: true },\n}\n"
  },
  {
    "path": "apps/dev/nextjs/package.json",
    "content": "{\n  \"name\": \"next-auth-app\",\n  \"version\": \"1.0.1\",\n  \"description\": \"Next.js + Auth.js Developer App\",\n  \"private\": true,\n  \"scripts\": {\n    \"clean\": \"rm -rf .next\",\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\"\n  },\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"next\": \"15.3.1\",\n    \"next-auth\": \"workspace:*\",\n    \"react\": \"19.0.0-rc-4c58fce7-20240904\",\n    \"react-dom\": \"19.0.0-rc-4c58fce7-20240904\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.2.23\",\n    \"@types/react-dom\": \"^18.2.8\"\n  }\n}\n"
  },
  {
    "path": "apps/dev/nextjs/pages/_app.tsx",
    "content": "import { SessionProvider, signIn, signOut, useSession } from \"next-auth/react\"\nimport \"./styles.css\"\nimport { Header } from \"components/header\"\nimport styles from \"components/header.module.css\"\nimport Footer from \"components/footer\"\n\nexport default function App({ Component, pageProps }) {\n  return (\n    <SessionProvider session={pageProps.session} basePath=\"/auth\">\n      <PagesHeader />\n      <Component {...pageProps} />\n      <Footer />\n    </SessionProvider>\n  )\n}\n\nfunction PagesHeader() {\n  const { data: session } = useSession()\n  return (\n    <Header\n      session={session}\n      signIn={\n        <button onClick={() => signIn()} className={styles.buttonPrimary}>\n          Sign in\n        </button>\n      }\n      signOut={\n        <button onClick={() => signOut()} className={styles.button}>\n          Sign out\n        </button>\n      }\n    />\n  )\n}\n"
  },
  {
    "path": "apps/dev/nextjs/pages/api/examples/protected.ts",
    "content": "import type { NextApiHandler } from \"next\"\n\nimport { auth } from \"../../../auth\"\n\nexport default async function handler(...args: Parameters<NextApiHandler>) {\n  const session = await auth(...args)\n  const res = args[1]\n  if (session?.user) {\n    // Do something with the session\n    return res.json(\"This is protected content.\")\n  }\n  res.status(401).json(\"You must be signed in.\")\n}\n"
  },
  {
    "path": "apps/dev/nextjs/pages/api/examples/session.ts",
    "content": "// This is an example of how to access a session from an API route\nimport { auth } from \"auth\"\n\nexport default async (req, res) => {\n  const session = await auth(req, res)\n  res.json(session)\n}\n"
  },
  {
    "path": "apps/dev/nextjs/pages/client.tsx",
    "content": "export default function Page() {\n  return (\n    <>\n      <h1>Client Side Rendering</h1>\n      <p>\n        This page uses the <strong>useSession()</strong> React Hook in the{\" \"}\n        <strong>&lt;/Header&gt;</strong> component.\n      </p>\n      <p>\n        The <strong>useSession()</strong> React Hook easy to use and allows\n        pages to render very quickly.\n      </p>\n      <p>\n        The advantage of this approach is that session state is shared between\n        pages by using the <strong>Provider</strong> in <strong>_app.js</strong>{\" \"}\n        so that navigation between pages using <strong>useSession()</strong> is\n        very fast.\n      </p>\n      <p>\n        The disadvantage of <strong>useSession()</strong> is that it requires\n        client side JavaScript.\n      </p>\n    </>\n  )\n}\n"
  },
  {
    "path": "apps/dev/nextjs/pages/credentials.tsx",
    "content": "import * as React from \"react\"\nimport { signIn, signOut, useSession } from \"next-auth/react\"\nimport { SignInResponse, SignOutResponse } from \"next-auth/react\"\n\nexport default function Page() {\n  const [response, setResponse] = React.useState<\n    SignInResponse | SignOutResponse\n  >()\n\n  const { data: session } = useSession()\n\n  if (session) {\n    return (\n      <>\n        <h1>Test different flows for Credentials logout</h1>\n        <span className=\"spacing\">Default: </span>\n        <button onClick={() => signOut()}>Logout</button>\n        <br />\n        <span className=\"spacing\">No redirect: </span>\n        <button onClick={() => signOut({ redirect: false }).then(setResponse)}>\n          Logout\n        </button>\n        <br />\n        <p>{response ? \"Response:\" : \"Session:\"}</p>\n        <pre style={{ background: \"#eee\", padding: 16 }}>\n          {JSON.stringify(response ?? session, null, 2)}\n        </pre>\n      </>\n    )\n  }\n\n  return (\n    <>\n      <h1>Test different flows for Credentials login</h1>\n      <span className=\"spacing\">Default: </span>\n      <button onClick={() => signIn(\"credentials\", { password: \"password\" })}>\n        Login\n      </button>\n      <br />\n      <span className=\"spacing\">No redirect: </span>\n      <button\n        onClick={() =>\n          signIn(\"credentials\", { redirect: false, password: \"password\" }).then(\n            setResponse\n          )\n        }\n      >\n        Login\n      </button>\n      <br />\n      <span className=\"spacing\">No redirect, wrong password: </span>\n      <button\n        onClick={() =>\n          signIn(\"credentials\", { redirect: false, password: \"wrong\" }).then(\n            setResponse\n          )\n        }\n      >\n        Login\n      </button>\n      <p>Response:</p>\n      <pre style={{ background: \"#eee\", padding: 16 }}>\n        {JSON.stringify(response, null, 2)}\n      </pre>\n    </>\n  )\n}\n"
  },
  {
    "path": "apps/dev/nextjs/pages/email.tsx",
    "content": "// eslint-disable-next-line no-use-before-define\nimport * as React from \"react\"\nimport { signIn, signOut, useSession } from \"next-auth/react\"\n\nexport default function Page() {\n  const [response, setResponse] =\n    React.useState<Awaited<ReturnType<typeof signIn>>>()\n  const [email, setEmail] = React.useState(\"\")\n\n  const handleChange = (event) => {\n    setEmail(event.target.value)\n  }\n\n  const handleLogin = (options) => async (event) => {\n    event.preventDefault()\n\n    if (options.redirect) {\n      return signIn(\"email\", options)\n    }\n    const response = await signIn(\"email\", options)\n    setResponse(response)\n  }\n\n  const handleLogout = (options) => async (event) => {\n    if (options.redirect) {\n      return signOut(options)\n    }\n    const response = await signOut(options)\n    setResponse(response)\n  }\n\n  const { data: session } = useSession()\n\n  if (session) {\n    return (\n      <>\n        <h1>Test different flows for Email logout</h1>\n        <span className=\"spacing\">Default:</span>\n        <button onClick={handleLogout({ redirect: true })}>Logout</button>\n        <br />\n        <span className=\"spacing\">No redirect:</span>\n        <button onClick={handleLogout({ redirect: false })}>Logout</button>\n        <br />\n        <p>Response:</p>\n        <pre style={{ background: \"#eee\", padding: 16 }}>\n          {JSON.stringify(response, null, 2)}\n        </pre>\n      </>\n    )\n  }\n\n  return (\n    <>\n      <h1>Test different flows for Email login</h1>\n      <label className=\"spacing\">\n        Email address:{\" \"}\n        <input\n          type=\"text\"\n          id=\"email\"\n          name=\"email\"\n          value={email}\n          onChange={handleChange}\n        />\n      </label>\n      <br />\n      <form onSubmit={handleLogin({ redirect: true, email })}>\n        <span className=\"spacing\">Default:</span>\n        <button type=\"submit\">Sign in with Email</button>\n      </form>\n      <form onSubmit={handleLogin({ redirect: false, email })}>\n        <span className=\"spacing\">No redirect:</span>\n        <button type=\"submit\">Sign in with Email</button>\n      </form>\n      <p>Response:</p>\n      <pre style={{ background: \"#eee\", padding: 16 }}>\n        {JSON.stringify(response, null, 2)}\n      </pre>\n    </>\n  )\n}\n"
  },
  {
    "path": "apps/dev/nextjs/pages/policy.tsx",
    "content": "export default function Page() {\n  return (\n    <>\n      <p>\n        This is an example site to demonstrate how to use{\" \"}\n        <a href=\"https://authjs.dev\">Auth.js</a> for authentication.\n      </p>\n      <h2>Terms of Service</h2>\n      <p>\n        THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n        OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n        MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n        IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n        CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n        TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n        SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n      </p>\n      <h2>Privacy Policy</h2>\n      <p>\n        Data provided to this site is exclusively used to support signing in and\n        is not passed to any third party services, other than via SMTP or OAuth\n        for the purposes of authentication.\n      </p>\n    </>\n  )\n}\n"
  },
  {
    "path": "apps/dev/nextjs/pages/protected-ssr.tsx",
    "content": "// This is an example of how to protect content using server rendering\nimport { auth } from \"../auth\"\nimport AccessDenied from \"components/access-denied\"\nimport { GetServerSideProps } from \"next\"\n\nexport default function Page({ content, session }) {\n  // If no session exists, display access denied message\n  if (!session) return <AccessDenied />\n\n  // If session exists, display content\n  return (\n    <>\n      <h1>Protected Page</h1>\n      <p>\n        <strong>{content}</strong>\n      </p>\n    </>\n  )\n}\n\nexport const getServerSideProps: GetServerSideProps = async (context) => {\n  const session = await auth(context)\n  if (session) {\n    // Note usually you don't need to fetch from an API route in getServerSideProps\n    // This is done here to demonstrate how you can fetch from a third-party API\n    // with a valid session. Likely you would also not pass cookies but an `Authorization` header\n    const hostname =\n      process.env.NEXTAUTH_URL ??\n      (process.env.VERCEL\n        ? \"https://next-auth-example-v5.vercel.app\"\n        : \"http://localhost:3000\")\n    const res = await fetch(`${hostname}/api/examples/protected`, {\n      headers: { cookie: context.req.headers.cookie ?? \"\" },\n    })\n    return { props: { session, content: await res.json() } }\n  }\n\n  return { props: {} }\n}\n"
  },
  {
    "path": "apps/dev/nextjs/pages/protected.tsx",
    "content": "import { useState, useEffect } from \"react\"\nimport { useSession } from \"next-auth/react\"\n\nexport default function Page() {\n  const { status } = useSession({ required: true })\n  const [content, setContent] = useState()\n\n  // Fetch content from protected route\n  useEffect(() => {\n    if (status === \"loading\") return\n    const fetchData = async () => {\n      const res = await fetch(\"/api/examples/protected\")\n      const json = await res.json()\n      if (json.content) {\n        setContent(json.content)\n      }\n    }\n    fetchData()\n  }, [status])\n\n  if (status === \"loading\") return \"Loading...\"\n\n  // If session exists, display content\n  return (\n    <>\n      <h1>Protected Page</h1>\n      <p>\n        <strong>{content}</strong>\n      </p>\n    </>\n  )\n}\n"
  },
  {
    "path": "apps/dev/nextjs/pages/styles.css",
    "content": "body {\n  font-family:\n    ui-sans-serif,\n    system-ui,\n    -apple-system,\n    BlinkMacSystemFont,\n    \"Segoe UI\",\n    Roboto,\n    \"Helvetica Neue\",\n    Arial,\n    \"Noto Sans\",\n    sans-serif,\n    \"Apple Color Emoji\",\n    \"Segoe UI Emoji\",\n    \"Segoe UI Symbol\",\n    \"Noto Color Emoji\";\n  padding: 0 1rem 1rem 1rem;\n  max-width: 680px;\n  margin: 0 auto;\n  background: #fff;\n  color: var(--color-text);\n}\n\nli,\np {\n  line-height: 1.5rem;\n}\n\na {\n  font-weight: 500;\n}\n\nhr {\n  border: 1px solid #ddd;\n}\n\niframe {\n  background: #ccc;\n  border: 1px solid #ccc;\n  height: 10rem;\n  width: 100%;\n  border-radius: 0.5rem;\n  filter: invert(1);\n}\n"
  },
  {
    "path": "apps/dev/nextjs/prisma/migrations/20231023165117_/migration.sql",
    "content": "-- CreateTable\nCREATE TABLE \"Account\" (\n    \"id\" TEXT NOT NULL PRIMARY KEY,\n    \"userId\" TEXT NOT NULL,\n    \"type\" TEXT NOT NULL,\n    \"provider\" TEXT NOT NULL,\n    \"providerAccountId\" TEXT NOT NULL,\n    \"refresh_token\" TEXT,\n    \"access_token\" TEXT,\n    \"expires_at\" INTEGER,\n    \"token_type\" TEXT,\n    \"scope\" TEXT,\n    \"id_token\" TEXT,\n    \"session_state\" TEXT,\n    CONSTRAINT \"Account_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES \"User\" (\"id\") ON DELETE RESTRICT ON UPDATE CASCADE\n);\n\n-- CreateTable\nCREATE TABLE \"Session\" (\n    \"id\" TEXT NOT NULL PRIMARY KEY,\n    \"sessionToken\" TEXT NOT NULL,\n    \"userId\" TEXT NOT NULL,\n    \"expires\" DATETIME NOT NULL,\n    CONSTRAINT \"Session_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES \"User\" (\"id\") ON DELETE RESTRICT ON UPDATE CASCADE\n);\n\n-- CreateTable\nCREATE TABLE \"User\" (\n    \"id\" TEXT NOT NULL PRIMARY KEY,\n    \"name\" TEXT,\n    \"email\" TEXT,\n    \"emailVerified\" DATETIME,\n    \"image\" TEXT\n);\n\n-- CreateTable\nCREATE TABLE \"VerificationToken\" (\n    \"identifier\" TEXT NOT NULL,\n    \"token\" TEXT NOT NULL,\n    \"expires\" DATETIME NOT NULL\n);\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"Account_provider_providerAccountId_key\" ON \"Account\"(\"provider\", \"providerAccountId\");\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"Session_sessionToken_key\" ON \"Session\"(\"sessionToken\");\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"User_email_key\" ON \"User\"(\"email\");\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"VerificationToken_token_key\" ON \"VerificationToken\"(\"token\");\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"VerificationToken_identifier_token_key\" ON \"VerificationToken\"(\"identifier\", \"token\");\n"
  },
  {
    "path": "apps/dev/nextjs/prisma/migrations/20240124035029_init/migration.sql",
    "content": "-- CreateTable\nCREATE TABLE \"Account\" (\n    \"userId\" TEXT NOT NULL,\n    \"type\" TEXT NOT NULL,\n    \"provider\" TEXT NOT NULL,\n    \"providerAccountId\" TEXT NOT NULL,\n    \"refresh_token\" TEXT,\n    \"access_token\" TEXT,\n    \"expires_at\" INTEGER,\n    \"token_type\" TEXT,\n    \"scope\" TEXT,\n    \"id_token\" TEXT,\n    \"session_state\" TEXT,\n\n    PRIMARY KEY (\"provider\", \"providerAccountId\"),\n    CONSTRAINT \"Account_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES \"User\" (\"id\") ON DELETE CASCADE ON UPDATE CASCADE\n);\n\n-- CreateTable\nCREATE TABLE \"Session\" (\n    \"id\" TEXT NOT NULL PRIMARY KEY,\n    \"sessionToken\" TEXT NOT NULL,\n    \"userId\" TEXT NOT NULL,\n    \"expires\" DATETIME NOT NULL,\n    CONSTRAINT \"Session_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES \"User\" (\"id\") ON DELETE RESTRICT ON UPDATE CASCADE\n);\n\n-- CreateTable\nCREATE TABLE \"User\" (\n    \"id\" TEXT NOT NULL PRIMARY KEY,\n    \"name\" TEXT,\n    \"email\" TEXT,\n    \"emailVerified\" DATETIME,\n    \"image\" TEXT\n);\n\n-- CreateTable\nCREATE TABLE \"VerificationToken\" (\n    \"identifier\" TEXT NOT NULL,\n    \"token\" TEXT NOT NULL,\n    \"expires\" DATETIME NOT NULL\n);\n\n-- CreateTable\nCREATE TABLE \"Authenticator\" (\n    \"id\" TEXT NOT NULL PRIMARY KEY,\n    \"credentialID\" TEXT NOT NULL,\n    \"userId\" TEXT NOT NULL,\n    \"providerAccountId\" TEXT NOT NULL,\n    \"credentialPublicKey\" TEXT NOT NULL,\n    \"counter\" INTEGER NOT NULL,\n    \"credentialDeviceType\" TEXT NOT NULL,\n    \"credentialBackedUp\" BOOLEAN NOT NULL,\n    \"transports\" TEXT,\n    CONSTRAINT \"Authenticator_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES \"User\" (\"id\") ON DELETE CASCADE ON UPDATE CASCADE\n);\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"Session_sessionToken_key\" ON \"Session\"(\"sessionToken\");\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"User_email_key\" ON \"User\"(\"email\");\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"VerificationToken_token_key\" ON \"VerificationToken\"(\"token\");\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"VerificationToken_identifier_token_key\" ON \"VerificationToken\"(\"identifier\", \"token\");\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"Authenticator_credentialID_key\" ON \"Authenticator\"(\"credentialID\");\n"
  },
  {
    "path": "apps/dev/nextjs/prisma/migrations/migration_lock.toml",
    "content": "# Please do not edit this file manually\n# It should be added in your version-control system (i.e. Git)\nprovider = \"sqlite\""
  },
  {
    "path": "apps/dev/nextjs/prisma/schema.prisma",
    "content": "datasource db {\n  provider = \"sqlite\"\n  url      = \"file:./dev.db\"\n}\n\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\nmodel Account {\n  userId            String\n  type              String\n  provider          String\n  providerAccountId String\n  refresh_token     String?\n  access_token      String?\n  expires_at        Int?\n  token_type        String?\n  scope             String?\n  id_token          String?\n  session_state     String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@id([provider, providerAccountId])\n}\n\nmodel Session {\n  id           String   @id @default(cuid())\n  sessionToken String   @unique\n  userId       String\n  expires      DateTime\n  user         User     @relation(fields: [userId], references: [id])\n}\n\nmodel User {\n  id            String    @id @default(cuid())\n  name          String?\n  email         String?   @unique\n  emailVerified DateTime?\n  image         String?\n  accounts      Account[]\n  sessions      Session[]\n  Authenticator Authenticator[]\n}\n\nmodel VerificationToken {\n  identifier String\n  token      String   @unique\n  expires    DateTime\n\n  @@unique([identifier, token])\n}\n\nmodel Authenticator {\n  id                   String  @id @default(cuid())\n  credentialID         String  @unique\n  userId               String\n  providerAccountId    String\n  credentialPublicKey  String\n  counter              Int\n  credentialDeviceType String\n  credentialBackedUp   Boolean\n  transports           String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n"
  },
  {
    "path": "apps/dev/nextjs/tests/signin.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\"\n\ntest(\"Sign in with Auth0\", async ({ page }) => {\n  // Go to NextAuth example app\n  await page.goto(\"https://next-auth-example.vercel.app\")\n\n  // Click 'Sign In'\n  await page.click(\"#__next > header > div > p > a\")\n\n  // Auth0 Login Provider\n  await page.click('body > div > div form[action*=\"auth0\"] > button')\n\n  // Enter Credentials (Username/Password Login) on Auth0 Widget\n  await page.type(\"#username\", process.env.AUTH0_USERNAME!)\n  await page.type(\"#password\", process.env.AUTH0_PASSWORD!)\n\n  // Snap a screenshot\n  // await page.screenshot({ path: \"1-auth0-login.png\", fullPage: true })\n\n  // Press submit on Auth0 form\n  await page.click('body > div > main > section > div button[type=\"submit\"]')\n\n  // Wait for next-auth example page login status header to appear\n  await page.waitForTimeout(2000)\n\n  // Snap a screenshot\n  // await page.screenshot({\n  //   path: \"2-next-auth-redirect-result.png\",\n  //   fullPage: false,\n  // })\n\n  // Check session object after successful login\n  const response = await page.goto(\n    \"https://next-auth-example.vercel.app/api/auth/session\"\n  )\n  const session = await response?.json()\n  expect(session?.user?.email).toBe(process.env.AUTH0_USERNAME)\n  // TODO: Check whole object with .toEqual()\n})\n"
  },
  {
    "path": "apps/dev/nextjs/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"esnext\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": false,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"incremental\": true,\n    \"jsx\": \"preserve\",\n    \"baseUrl\": \".\",\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"strictNullChecks\": true\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "apps/dev/qwik/.gitignore",
    "content": "# Build\n/dist\n/lib\n/lib-types\n/server\n\n# Development\nnode_modules\n*.local\n\n# Cache\n.cache\n.mf\n.rollup.cache\ntsconfig.tsbuildinfo\n\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\n# Editor\n.vscode/*\n!.vscode/launch.json\n!.vscode/*.code-snippets\n\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n\n# Yarn\n.yarn/*\n!.yarn/releases\n"
  },
  {
    "path": "apps/dev/qwik/README.md",
    "content": "# Qwik City App ⚡️\n\n- [Qwik Docs](https://qwik.dev/)\n- [Discord](https://qwik.dev/chat)\n- [Qwik GitHub](https://github.com/QwikDev/qwik)\n- [@QwikDev](https://twitter.com/QwikDev)\n- [Vite](https://vitejs.dev/)\n\n---\n\n## Project Structure\n\nThis project is using Qwik with [QwikCity](https://qwik.dev/qwikcity/overview/). QwikCity is just an extra set of tools on top of Qwik to make it easier to build a full site, including directory-based routing, layouts, and more.\n\nInside your project, you'll see the following directory structure:\n\n```\n├── public/\n│   └── ...\n└── src/\n    ├── components/\n    │   └── ...\n    └── routes/\n        └── ...\n```\n\n- `src/routes`: Provides the directory-based routing, which can include a hierarchy of `layout.tsx` layout files, and an `index.tsx` file as the page. Additionally, `index.ts` files are endpoints. Please see the [routing docs](https://qwik.dev/qwikcity/routing/overview/) for more info.\n\n- `src/components`: Recommended directory for components.\n\n- `public`: Any static assets, like images, can be placed in the public directory. Please see the [Vite public directory](https://vitejs.dev/guide/assets.html#the-public-directory) for more info.\n\n## Add Integrations and deployment\n\nUse the `pnpm qwik add` command to add additional integrations. Some examples of integrations includes: Cloudflare, Netlify or Express Server, and the [Static Site Generator (SSG)](https://qwik.dev/qwikcity/guides/static-site-generation/).\n\n```shell\npnpm qwik add # or `pnpm qwik add`\n```\n\n## Development\n\nDevelopment mode uses [Vite's development server](https://vitejs.dev/). The `dev` command will server-side render (SSR) the output during development.\n\n```shell\nnpm start # or `pnpm start`\n```\n\n> Note: during dev mode, Vite may request a significant number of `.js` files. This does not represent a Qwik production build.\n\n## Preview\n\nThe preview command will create a production build of the client modules, a production build of `src/entry.preview.tsx`, and run a local server. The preview server is only for convenience to preview a production build locally and should not be used as a production server.\n\n```shell\npnpm preview # or `pnpm preview`\n```\n\n## Production\n\nThe production build will generate client and server modules by running both client and server build commands. The build command will use TypeScript to run a type check on the source code.\n\n```shell\npnpm build # or `pnpm build`\n```\n"
  },
  {
    "path": "apps/dev/qwik/package.json",
    "content": "{\n  \"name\": \"qwik-auth-app\",\n  \"description\": \"Qwik + Auth.js Developer app\",\n  \"engines\": {\n    \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\n  },\n  \"engines-annotation\": \"Mostly required by sharp which needs a Node-API v9 compatible runtime\",\n  \"private\": true,\n  \"trustedDependencies\": [\n    \"sharp\"\n  ],\n  \"trustedDependencies-annotation\": \"Needed for bun to allow running install scripts\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"qwik build\",\n    \"build.client\": \"vite build\",\n    \"build.preview\": \"vite build --ssr src/entry.preview.tsx\",\n    \"build.types\": \"tsc --incremental --noEmit\",\n    \"deploy\": \"echo 'Run \\\"npm run qwik add\\\" to install a server adapter'\",\n    \"dev\": \"vite --mode ssr\",\n    \"dev.debug\": \"node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force\",\n    \"fmt\": \"prettier --write .\",\n    \"fmt.check\": \"prettier --check .\",\n    \"preview\": \"qwik build preview && vite preview --open\",\n    \"start\": \"vite --open --mode ssr\",\n    \"qwik\": \"qwik\"\n  },\n  \"devDependencies\": {\n    \"@auth/qwik\": \"workspace:*\",\n    \"@builder.io/qwik\": \"^1.5.5\",\n    \"@builder.io/qwik-city\": \"^1.5.5\",\n    \"@types/eslint\": \"^8.56.10\",\n    \"@types/node\": \"^20.12.7\",\n    \"@typescript-eslint/eslint-plugin\": \"^7.7.1\",\n    \"@typescript-eslint/parser\": \"^7.7.1\",\n    \"eslint\": \"^8.57.0\",\n    \"eslint-plugin-qwik\": \"^1.5.5\",\n    \"prettier\": \"^3.2.5\",\n    \"typescript\": \"5.4.5\",\n    \"undici\": \"*\",\n    \"vite\": \"^5.2.10\",\n    \"vite-tsconfig-paths\": \"^4.2.1\"\n  }\n}\n"
  },
  {
    "path": "apps/dev/qwik/public/manifest.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/web-manifest-combined.json\",\n  \"name\": \"qwik-project-name\",\n  \"short_name\": \"Welcome to Qwik\",\n  \"start_url\": \".\",\n  \"display\": \"standalone\",\n  \"background_color\": \"#fff\",\n  \"description\": \"A Qwik project app.\"\n}\n"
  },
  {
    "path": "apps/dev/qwik/public/robots.txt",
    "content": ""
  },
  {
    "path": "apps/dev/qwik/qwik.env.d.ts",
    "content": "// This file can be used to add references for global types like `vite/client`.\n\n// Add global `vite/client` types. For more info, see: https://vitejs.dev/guide/features#client-types\n/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "apps/dev/qwik/src/components/router-head/router-head.tsx",
    "content": "import { component$ } from \"@builder.io/qwik\"\nimport { useDocumentHead, useLocation } from \"@builder.io/qwik-city\"\n\n/**\n * The RouterHead component is placed inside of the document `<head>` element.\n */\nexport const RouterHead = component$(() => {\n  const head = useDocumentHead()\n  const loc = useLocation()\n\n  return (\n    <>\n      <title>{head.title}</title>\n\n      <link rel=\"canonical\" href={loc.url.href} />\n      <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n      <link rel=\"icon\" type=\"image/svg+xml\" href=\"/favicon.svg\" />\n\n      {head.meta.map((m) => (\n        <meta key={m.key} {...m} />\n      ))}\n\n      {head.links.map((l) => (\n        <link key={l.key} {...l} />\n      ))}\n\n      {head.styles.map((s) => (\n        <style\n          key={s.key}\n          {...s.props}\n          {...(s.props?.dangerouslySetInnerHTML\n            ? {}\n            : { dangerouslySetInnerHTML: s.style })}\n        />\n      ))}\n\n      {head.scripts.map((s) => (\n        <script\n          key={s.key}\n          {...s.props}\n          {...(s.props?.dangerouslySetInnerHTML\n            ? {}\n            : { dangerouslySetInnerHTML: s.script })}\n        />\n      ))}\n    </>\n  )\n})\n"
  },
  {
    "path": "apps/dev/qwik/src/entry.dev.tsx",
    "content": "/*\n * WHAT IS THIS FILE?\n *\n * Development entry point using only client-side modules:\n * - Do not use this mode in production!\n * - No SSR\n * - No portion of the application is pre-rendered on the server.\n * - All of the application is running eagerly in the browser.\n * - More code is transferred to the browser than in SSR mode.\n * - Optimizer/Serialization/Deserialization code is not exercised!\n */\nimport { render, type RenderOptions } from \"@builder.io/qwik\"\nimport Root from \"./root\"\n\nexport default function (opts: RenderOptions) {\n  return render(document, <Root />, opts)\n}\n"
  },
  {
    "path": "apps/dev/qwik/src/entry.preview.tsx",
    "content": "/*\n * WHAT IS THIS FILE?\n *\n * It's the bundle entry point for `npm run preview`.\n * That is, serving your app built in production mode.\n *\n * Feel free to modify this file, but don't remove it!\n *\n * Learn more about Vite's preview command:\n * - https://vitejs.dev/config/preview-options.html#preview-options\n *\n */\nimport { createQwikCity } from \"@builder.io/qwik-city/middleware/node\"\nimport qwikCityPlan from \"@qwik-city-plan\"\n// make sure qwikCityPlan is imported before entry\nimport render from \"./entry.ssr\"\n\n/**\n * The default export is the QwikCity adapter used by Vite preview.\n */\nexport default createQwikCity({ render, qwikCityPlan })\n"
  },
  {
    "path": "apps/dev/qwik/src/entry.ssr.tsx",
    "content": "/**\n * WHAT IS THIS FILE?\n *\n * SSR entry point, in all cases the application is rendered outside the browser, this\n * entry point will be the common one.\n *\n * - Server (express, cloudflare...)\n * - npm run start\n * - npm run preview\n * - npm run build\n *\n */\nimport {\n  renderToStream,\n  type RenderToStreamOptions,\n} from \"@builder.io/qwik/server\"\nimport { manifest } from \"@qwik-client-manifest\"\nimport Root from \"./root\"\n\nexport default function (opts: RenderToStreamOptions) {\n  return renderToStream(<Root />, {\n    manifest,\n    ...opts,\n    // Use container attributes to set attributes on the html tag.\n    containerAttributes: {\n      lang: \"en-us\",\n      ...opts.containerAttributes,\n    },\n    serverData: {\n      ...opts.serverData,\n    },\n  })\n}\n"
  },
  {
    "path": "apps/dev/qwik/src/global.css",
    "content": ""
  },
  {
    "path": "apps/dev/qwik/src/root.tsx",
    "content": "import { component$ } from \"@builder.io/qwik\"\nimport {\n  QwikCityProvider,\n  RouterOutlet,\n  ServiceWorkerRegister,\n} from \"@builder.io/qwik-city\"\nimport { RouterHead } from \"./components/router-head/router-head\"\n\nimport \"./global.css\"\n\nexport default component$(() => {\n  /**\n   * The root of a QwikCity site always start with the <QwikCityProvider> component,\n   * immediately followed by the document's <head> and <body>.\n   *\n   * Don't remove the `<head>` and `<body>` elements.\n   */\n\n  return (\n    <QwikCityProvider>\n      <head>\n        <meta charSet=\"utf-8\" />\n        <link rel=\"manifest\" href=\"/manifest.json\" />\n        <RouterHead />\n      </head>\n      <body lang=\"en\">\n        <RouterOutlet />\n        <ServiceWorkerRegister />\n      </body>\n    </QwikCityProvider>\n  )\n})\n"
  },
  {
    "path": "apps/dev/qwik/src/routes/index.tsx",
    "content": "import { component$ } from \"@builder.io/qwik\"\nimport { Form, type RequestHandler } from \"@builder.io/qwik-city\"\nimport { useSession, useSignIn, useSignOut } from \"./plugin@auth\"\n\nexport const onRequest: RequestHandler = (event) => {\n  const session = event.sharedMap.get(\"session\")\n  if (!session || new Date(session.expires) < new Date()) {\n    console.log(\"Not authorized. Redirect or throw error here.\")\n  }\n}\n\nexport default component$(() => {\n  const signIn = useSignIn()\n  const signOut = useSignOut()\n  const session = useSession()\n  return (\n    <>\n      <Form action={signIn}>\n        <input type=\"hidden\" name=\"providerId\" value=\"github\" />\n        <input\n          type=\"hidden\"\n          name=\"options.redirectTo\"\n          value=\"http://qwik-auth-example.com/dashboard\"\n        />\n        <button>Sign In</button>\n      </Form>\n      Session: {JSON.stringify(session.value)}\n      <br />\n      <button onClick$={() => signOut.submit({ redirectTo: \"/\" })}>\n        Sign Out\n      </button>\n    </>\n  )\n})\n"
  },
  {
    "path": "apps/dev/qwik/src/routes/layout.tsx",
    "content": "import { component$, Slot } from \"@builder.io/qwik\"\nimport type { RequestHandler } from \"@builder.io/qwik-city\"\n\nexport const onGet: RequestHandler = async ({ cacheControl }) => {\n  // Control caching for this request for best performance and to reduce hosting costs:\n  // https://qwik.dev/docs/caching/\n  cacheControl({\n    // Always serve a cached response by default, up to a week stale\n    staleWhileRevalidate: 60 * 60 * 24 * 7,\n    // Max once every 5 seconds, revalidate on the server to get a fresh version of this page\n    maxAge: 5,\n  })\n}\n\nexport default component$(() => {\n  return <Slot />\n})\n"
  },
  {
    "path": "apps/dev/qwik/src/routes/plugin@auth.ts",
    "content": "import { DefaultSession, QwikAuth$ } from \"@auth/qwik\"\nimport GitHub from \"@auth/qwik/providers/github\"\n\ndeclare module \"@auth/qwik\" {\n  interface Session {\n    user: {\n      roles: string[]\n    } & DefaultSession[\"user\"]\n  }\n}\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [GitHub],\n  })\n)\n"
  },
  {
    "path": "apps/dev/qwik/src/routes/service-worker.ts",
    "content": "/*\n * WHAT IS THIS FILE?\n *\n * The service-worker.ts file is used to have state of the art prefetching.\n * https://qwik.dev/qwikcity/prefetching/overview/\n *\n * Qwik uses a service worker to speed up your site and reduce latency, ie, not used in the traditional way of offline.\n * You can also use this file to add more functionality that runs in the service worker.\n */\nimport { setupServiceWorker } from \"@builder.io/qwik-city/service-worker\"\n\nsetupServiceWorker()\n\naddEventListener(\"install\", () => self.skipWaiting())\n\naddEventListener(\"activate\", () => self.clients.claim())\n\ndeclare const self: ServiceWorkerGlobalScope\n"
  },
  {
    "path": "apps/dev/qwik/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"target\": \"ES2017\",\n    \"module\": \"ES2022\",\n    \"lib\": [\"es2022\", \"DOM\", \"WebWorker\", \"DOM.Iterable\"],\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"@builder.io/qwik\",\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"resolveJsonModule\": true,\n    \"moduleResolution\": \"Bundler\",\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true,\n    \"incremental\": true,\n    \"isolatedModules\": true,\n    \"outDir\": \"tmp\",\n    \"noEmit\": true,\n    \"paths\": {\n      \"~/*\": [\"./src/*\"]\n    }\n  },\n  \"include\": [\"src\", \"./*.d.ts\", \"./*.config.ts\"]\n}\n"
  },
  {
    "path": "apps/dev/qwik/vite.config.ts",
    "content": "/**\n * This is the base config for vite.\n * When building, the adapter config is used which loads this file and extends it.\n */\nimport { qwikCity } from \"@builder.io/qwik-city/vite\"\nimport { qwikVite } from \"@builder.io/qwik/optimizer\"\nimport { defineConfig, type UserConfig } from \"vite\"\nimport tsconfigPaths from \"vite-tsconfig-paths\"\nimport pkg from \"./package.json\"\n\ntype PkgDep = Record<string, string>\nconst { dependencies = {}, devDependencies = {} } = pkg as any as {\n  dependencies: PkgDep\n  devDependencies: PkgDep\n  [key: string]: unknown\n}\nerrorOnDuplicatesPkgDeps(devDependencies, dependencies)\n\n/**\n * Note that Vite normally starts from `index.html` but the qwikCity plugin makes start at `src/entry.ssr.tsx` instead.\n */\nexport default defineConfig(({ command, mode }): UserConfig => {\n  return {\n    plugins: [qwikCity(), qwikVite(), tsconfigPaths()],\n    // This tells Vite which dependencies to pre-build in dev mode.\n    optimizeDeps: {\n      // Put problematic deps that break bundling here, mostly those with binaries.\n      // For example ['better-sqlite3'] if you use that in server functions.\n      exclude: [],\n    },\n\n    /**\n     * This is an advanced setting. It improves the bundling of your server code. To use it, make sure you understand when your consumed packages are dependencies or dev dependencies. (otherwise things will break in production)\n     */\n    // ssr:\n    //   command === \"build\" && mode === \"production\"\n    //     ? {\n    //         // All dev dependencies should be bundled in the server build\n    //         noExternal: Object.keys(devDependencies),\n    //         // Anything marked as a dependency will not be bundled\n    //         // These should only be production binary deps (including deps of deps), CLI deps, and their module graph\n    //         // If a dep-of-dep needs to be external, add it here\n    //         // For example, if something uses `bcrypt` but you don't have it as a dep, you can write\n    //         // external: [...Object.keys(dependencies), 'bcrypt']\n    //         external: Object.keys(dependencies),\n    //       }\n    //     : undefined,\n\n    server: {\n      headers: {\n        // Don't cache the server response in dev mode\n        \"Cache-Control\": \"public, max-age=0\",\n      },\n    },\n    preview: {\n      headers: {\n        // Do cache the server response in preview (non-adapter production build)\n        \"Cache-Control\": \"public, max-age=600\",\n      },\n    },\n  }\n})\n\n// *** utils ***\n\n/**\n * Function to identify duplicate dependencies and throw an error\n * @param {Object} devDependencies - List of development dependencies\n * @param {Object} dependencies - List of production dependencies\n */\nfunction errorOnDuplicatesPkgDeps(\n  devDependencies: PkgDep,\n  dependencies: PkgDep\n) {\n  let msg = \"\"\n  // Create an array 'duplicateDeps' by filtering devDependencies.\n  // If a dependency also exists in dependencies, it is considered a duplicate.\n  const duplicateDeps = Object.keys(devDependencies).filter(\n    (dep) => dependencies[dep]\n  )\n\n  // include any known qwik packages\n  const qwikPkg = Object.keys(dependencies).filter((value) =>\n    /qwik/i.test(value)\n  )\n\n  // any errors for missing \"qwik-city-plan\"\n  // [PLUGIN_ERROR]: Invalid module \"@qwik-city-plan\" is not a valid package\n  msg = `Move qwik packages ${qwikPkg.join(\", \")} to devDependencies`\n\n  if (qwikPkg.length > 0) {\n    throw new Error(msg)\n  }\n\n  // Format the error message with the duplicates list.\n  // The `join` function is used to represent the elements of the 'duplicateDeps' array as a comma-separated string.\n  msg = `\n    Warning: The dependency \"${duplicateDeps.join(\n      \", \"\n    )}\" is listed in both \"devDependencies\" and \"dependencies\".\n    Please move the duplicated dependencies to \"devDependencies\" only and remove it from \"dependencies\"\n  `\n\n  // Throw an error with the constructed message.\n  if (duplicateDeps.length > 0) {\n    throw new Error(msg)\n  }\n}\n"
  },
  {
    "path": "apps/dev/sveltekit/.env.example",
    "content": "AUTH_GITHUB_ID=\nAUTH_GITHUB_SECRET=\n# On UNIX systems you can use `openssl rand -hex 32` or \n# https://generate-secret.vercel.app/32 to generate a secret.\nAUTH_SECRET=\n"
  },
  {
    "path": "apps/dev/sveltekit/.eslintignore",
    "content": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/package\n.env\n.env.*\n!.env.example\n\n# Ignore files for PNPM, NPM and YARN\npnpm-lock.yaml\npackage-lock.json\nyarn.lock\n"
  },
  {
    "path": "apps/dev/sveltekit/.eslintrc.cjs",
    "content": "module.exports = {\n  root: true,\n  parser: \"@typescript-eslint/parser\",\n  extends: [\n    \"eslint:recommended\",\n    \"plugin:@typescript-eslint/recommended\",\n    \"prettier\",\n  ],\n  plugins: [\"@typescript-eslint\"],\n  ignorePatterns: [\"*.cjs\"],\n  overrides: [\n    {\n      files: [\"*.svelte\"],\n      parser: \"svelte-eslint-parser\",\n      parserOptions: {\n        parser: \"@typescript-eslint/parser\",\n      },\n    },\n  ],\n  parserOptions: {\n    sourceType: \"module\",\n    ecmaVersion: 2020,\n    extraFileExtensions: [\".svelte\"],\n  },\n  env: {\n    browser: true,\n    es2017: true,\n    node: true,\n  },\n}\n"
  },
  {
    "path": "apps/dev/sveltekit/.gitignore",
    "content": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/package\n.env\n.env.*\n!.env.example\n.vercel\n.output\nvite.config.js.timestamp-*\nvite.config.ts.timestamp-*\ntmp-unstorage\n"
  },
  {
    "path": "apps/dev/sveltekit/.prettierignore",
    "content": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/package\n.env\n.env.*\n!.env.example\n\n# Ignore files for PNPM, NPM and YARN\npnpm-lock.yaml\npackage-lock.json\nyarn.lock\n"
  },
  {
    "path": "apps/dev/sveltekit/.prettierrc",
    "content": "{\n  \"semi\": false,\n  \"plugins\": [\"prettier-plugin-svelte\"],\n  \"overrides\": [{ \"files\": \"*.svelte\", \"options\": { \"parser\": \"svelte\" } }]\n}\n"
  },
  {
    "path": "apps/dev/sveltekit/README.md",
    "content": "> The example repository is maintained from a [monorepo](https://github.com/nextauthjs/next-auth/tree/main/apps/example-sveltekit). Pull Requests should be opened against [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth).\n\n<p align=\"center\">\n   <br/>\n   <a href=\"https://authjs.dev\" target=\"_blank\"><img width=\"150px\" src=\"https://authjs.dev/img/logo-sm.png\" /></a>\n   <h3 align=\"center\">Auth.js Example App with <a href=\"https://kit.svelte.dev\">SvelteKit</a></h3>\n   <p align=\"center\">\n   Open Source. Full Stack. Own Your Data.\n   </p>\n   <p align=\"center\" style=\"align: center;\">\n      <a href=\"https://npm.im/@auth/sveltekit\">\n        <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/sveltekit?color=green&label=@auth/sveltekit&style=flat-square\">\n      </a>\n      <a href=\"https://bundlephobia.com/result?p=sveltekit-auth-example\">\n        <img src=\"https://img.shields.io/bundlephobia/minzip/@auth/sveltekit?label=size&style=flat-square\" alt=\"Bundle Size\"/>\n      </a>\n      <a href=\"https://www.npmtrends.com/@auth/sveltekit\">\n        <img src=\"https://img.shields.io/npm/dm/@auth/sveltekit?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n      </a>\n      <a href=\"https://npm.im/next-auth\">\n        <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n      </a>\n   </p>\n</p>\n\n# Documentation\n\n- [sveltekit.authjs.dev](https://sveltekit.authjs.dev)\n"
  },
  {
    "path": "apps/dev/sveltekit/package.json",
    "content": "{\n  \"name\": \"sveltekit-auth-app\",\n  \"version\": \"1.0.0\",\n  \"description\": \"SvelteKit + Auth.js Developer app\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"vite dev\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\",\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    \"@sveltejs/adapter-auto\": \"next\",\n    \"@sveltejs/kit\": \"^2.5.7\",\n    \"@sveltejs/vite-plugin-svelte\": \"^3.0.0\",\n    \"svelte\": \"^4\",\n    \"svelte-check\": \"2.10.2\",\n    \"typescript\": \"5.2.2\"\n  },\n  \"dependencies\": {\n    \"@auth/sveltekit\": \"workspace:*\",\n    \"@auth/unstorage-adapter\": \"workspace:*\",\n    \"unstorage\": \"^1.10.2\"\n  },\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "apps/dev/sveltekit/src/app.d.ts",
    "content": "/// <reference types=\"@auth/sveltekit\" />\n"
  },
  {
    "path": "apps/dev/sveltekit/src/app.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <link rel=\"icon\" href=\"%sveltekit.assets%/favicon.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width\" />\n    %sveltekit.head%\n  </head>\n\n  <body>\n    <div>%sveltekit.body%</div>\n  </body>\n</html>\n"
  },
  {
    "path": "apps/dev/sveltekit/src/auth.ts",
    "content": "import { SvelteKitAuth } from \"@auth/sveltekit\"\nimport GitHub from \"@auth/sveltekit/providers/github\"\nimport Credentials from \"@auth/sveltekit/providers/credentials\"\nimport Facebook from \"@auth/sveltekit/providers/facebook\"\nimport Discord from \"@auth/sveltekit/providers/discord\"\nimport Google from \"@auth/sveltekit/providers/google\"\nimport Passkey from \"@auth/sveltekit/providers/passkey\"\nimport { createStorage } from \"unstorage\"\nimport { UnstorageAdapter } from \"@auth/unstorage-adapter\"\nimport fsDriver from \"unstorage/drivers/fs\"\nimport { dev } from \"$app/environment\"\n\nconst storage = createStorage({\n  driver: fsDriver({ base: \"./tmp-unstorage\" }),\n})\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  debug: dev ? true : false,\n  adapter: UnstorageAdapter(storage),\n  experimental: {\n    enableWebAuthn: true,\n  },\n  providers: [\n    Credentials({\n      credentials: { password: { label: \"Password\", type: \"password\" } },\n      async authorize(credentials) {\n        if (credentials.password !== \"password\") return null\n        return {\n          name: \"Fill Murray\",\n          email: \"bill@fillmurray.com\",\n          image: `https://api.dicebear.com/9.x/thumbs/svg?seed=234173&randomizeIds=true`,\n          id: \"1\",\n        }\n      },\n    }),\n    GitHub,\n    Google,\n    Facebook,\n    Discord,\n    Passkey({\n      formFields: {\n        email: {\n          label: \"Username\",\n          required: true,\n          autocomplete: \"username webauthn\",\n        },\n      },\n    }),\n  ],\n  theme: {\n    logo: \"https://authjs.dev/img/logo-sm.png\",\n  },\n})\n"
  },
  {
    "path": "apps/dev/sveltekit/src/components/header.svelte",
    "content": "<script lang=\"ts\">\n  import { page } from \"$app/stores\"\n  import { SignIn, SignOut } from \"@auth/sveltekit/components\"\n</script>\n\n<header>\n  <nav class=\"nojs-show loaded\">\n    <div class=\"nav-left\">\n      <img\n        alt=\"Avatar\"\n        src={$page.data.session?.user?.image ??\n          `https://api.dicebear.com/9.x/thumbs/svg?seed=${Math.floor(Math.random() * 100000) + 1}&randomizeIds=true`}\n        class=\"avatar\"\n      />\n    </div>\n    <div class=\"nav-right\">\n      {#if $page.data.session}\n        <span class=\"header-text\">\n          <small>Signed in as</small><br />\n          <strong>\n            {$page.data.session.user?.email ?? $page.data.session.user?.name}\n          </strong>\n        </span>\n        <SignOut>\n          <div class=\"buttonPrimary\" slot=\"submitButton\">Sign out</div>\n        </SignOut>\n      {:else}\n        <span class=\"header-text\">You are not signed in</span>\n        <SignIn>\n          <div class=\"buttonPrimary\" slot=\"submitButton\">Sign in</div>\n        </SignIn>\n      {/if}\n    </div>\n  </nav>\n  <div class=\"links\">\n    <a class=\"linkItem\" href=\"/\">Home</a>\n    <a class=\"linkItem\" href=\"/protected\">Protected</a>\n  </div>\n</header>\n"
  },
  {
    "path": "apps/dev/sveltekit/src/hooks.server.ts",
    "content": "export { handle } from \"./auth\"\n"
  },
  {
    "path": "apps/dev/sveltekit/src/routes/+layout.server.ts",
    "content": "import type { LayoutServerLoad } from \"./$types\"\n\nexport const load: LayoutServerLoad = async (event) => {\n  return {\n    session: await event.locals.auth(),\n  }\n}\n"
  },
  {
    "path": "apps/dev/sveltekit/src/routes/+layout.svelte",
    "content": "<script lang=\"ts\">\n  import Header from \"$components/header.svelte\"\n  import \"../style.css\"\n</script>\n\n<Header />\n<slot />\n"
  },
  {
    "path": "apps/dev/sveltekit/src/routes/+page.svelte",
    "content": "<script lang=\"ts\">\n  import { page } from \"$app/stores\"\n  import { SignIn } from \"@auth/sveltekit/components\"\n  import { signIn } from \"@auth/sveltekit/client\"\n\n  let password = \"\"\n</script>\n\n<h1>SvelteKit Auth Example</h1>\n<div class=\"container\">\n  <p>\n    This is an example site to demonstrate how to use\n    <a href=\"https://kit.svelte.dev/\">SvelteKit</a>\n    with <a href=\"https://sveltekit.authjs.dev\">SvelteKit Auth</a> for authentication.\n  </p>\n\n  <div class=\"session-code\">\n    <div class=\"session-code-header\">\n      <h3>Session</h3>\n    </div>\n    <div class=\"session-code-body\">\n      <pre>\n{JSON.stringify($page.data.session, null, 2)}\n      </pre>\n    </div>\n  </div>\n\n  <div class=\"login-cards\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3>Server</h3>\n      </div>\n      <div class=\"card-body\">\n        <p>\n          These actions are all using the components exported from\n          <code>@auth/sveltekit/components</code> to run via form actions.\n        </p>\n        <div class=\"actions\">\n          <SignIn provider=\"github\">\n            <span slot=\"submitButton\">\n              <img\n                src=\"https://authjs.dev/img/providers/github.svg\"\n                alt=\"GitHub Logo\"\n                width=\"20\"\n                height=\"20\"\n              />\n              GitHub\n            </span>\n          </SignIn>\n          <SignIn provider=\"discord\">\n            <span slot=\"submitButton\">\n              <img\n                src=\"https://authjs.dev/img/providers/discord.svg\"\n                alt=\"Discord Logo\"\n                width=\"20\"\n                height=\"20\"\n              />\n              Discord\n            </span>\n          </SignIn>\n          <div class=\"or-split\">or</div>\n          <SignIn provider=\"credentials\">\n            <span slot=\"submitButton\">Sign In with Credentials</span>\n            <div slot=\"credentials\" style=\"width: 100%;\">\n              <div class=\"wrapper-form\" style=\"width: 100%;\">\n                <div class=\"input-wrapper\">\n                  <label for=\"server-password\">Password</label>\n                  <input\n                    type=\"password\"\n                    id=\"server-password\"\n                    name=\"password\"\n                    placeholder=\"password\"\n                    required\n                  />\n                </div>\n              </div>\n            </div>\n          </SignIn>\n        </div>\n      </div>\n    </div>\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3>Client</h3>\n      </div>\n      <div class=\"card-body\">\n        <p>\n          These actions are all using the methods exported from\n          <code>@auth/sveltekit/client</code>\n        </p>\n        <div class=\"actions\">\n          <div class=\"wrapper-form social-btn\">\n            <button on:click={() => signIn(\"github\")}>\n              <img\n                src=\"https://authjs.dev/img/providers/github.svg\"\n                alt=\"GitHub Logo\"\n                width=\"20\"\n                height=\"20\"\n              />\n              GitHub\n            </button>\n          </div>\n          <div class=\"wrapper-form social-btn\">\n            <button on:click={() => signIn(\"discord\")}>\n              <img\n                src=\"https://authjs.dev/img/providers/discord.svg\"\n                alt=\"Discord Logo\"\n                width=\"20\"\n                height=\"20\"\n              />\n              Discord\n            </button>\n          </div>\n          <div class=\"or-split\">or</div>\n          <div class=\"wrapper-form\">\n            <div class=\"input-wrapper\">\n              <label for=\"client-password\">Password</label>\n              <input\n                bind:value={password}\n                type=\"password\"\n                placeholder=\"password\"\n                id=\"client-password\"\n                name=\"password\"\n                required\n              />\n            </div>\n            <button on:click={() => signIn(\"credentials\", { password })}>\n              Sign In with Credentials\n            </button>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "apps/dev/sveltekit/src/routes/protected/+page.svelte",
    "content": "<script lang=\"ts\">\n  import { page } from \"$app/stores\"\n</script>\n\n{#if $page.data.session}\n  <h1>Protected page</h1>\n  <p>\n    This is a protected content. You can access this content because you are\n    signed in.\n  </p>\n  <p>Session expiry: {$page.data.session?.expires}</p>\n{:else}\n  <h1>Access Denied</h1>\n{/if}\n"
  },
  {
    "path": "apps/dev/sveltekit/src/routes/signin/+page.server.ts",
    "content": "import { signIn } from \"$/auth\"\nimport type { Actions } from \"./$types\"\n\nexport const actions = { default: signIn } satisfies Actions\n"
  },
  {
    "path": "apps/dev/sveltekit/src/routes/signout/+page.server.ts",
    "content": "import { signOut } from \"$/auth\"\nimport type { Actions } from \"./$types\"\n\nexport const actions = { default: signOut } satisfies Actions\n"
  },
  {
    "path": "apps/dev/sveltekit/src/style.css",
    "content": "body {\n  font-family:\n    ui-sans-serif,\n    system-ui,\n    -apple-system,\n    BlinkMacSystemFont,\n    \"Segoe UI\",\n    Roboto,\n    \"Helvetica Neue\",\n    Arial,\n    \"Noto Sans\",\n    sans-serif,\n    \"Apple Color Emoji\",\n    \"Segoe UI Emoji\",\n    \"Segoe UI Symbol\",\n    \"Noto Color Emoji\";\n  max-width: 960px;\n  margin: 0 auto;\n  background: #fff;\n  color: var(--color-text);\n  height: 100dvh;\n  display: flex;\n  flex-direction: column;\n}\n\ncode {\n  background: #ddd;\n  padding: 6px;\n  border-radius: 0.25rem;\n}\n\nli,\np {\n  line-height: 1.5rem;\n  margin: 0;\n}\n\na {\n  font-weight: 500;\n}\n\niframe {\n  background: #ccc;\n  border: 1px solid #ccc;\n  height: 10rem;\n  width: 100%;\n  border-radius: 0.5rem;\n  filter: invert(1);\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  margin: 0px;\n}\n\nmain {\n  flex-grow: 1;\n\n  @media screen and (max-width: 960px) {\n    padding: 0 1rem;\n  }\n}\n\n/* Index Page Cards */\n.container {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  align-items: stretch;\n  justify-content: flex-start;\n}\n\n.session-code {\n  background-color: #f3f3f3;\n  border-radius: 0.75rem;\n  display: flex;\n  flex-direction: column;\n  justify-content: start;\n\n  .session-code-header {\n    border-radius: 0.75rem 0.75rem 0 0;\n    background-color: #dfdfdf;\n    padding: 1.1rem;\n  }\n\n  .session-code-body {\n    display: flex;\n    flex-direction: column;\n    padding: 1.1rem;\n    gap: 2rem;\n  }\n}\n\n.login-cards {\n  display: flex;\n  gap: 2rem;\n}\n\n.card {\n  width: calc(50% - 1rem);\n  border-radius: 0.75rem;\n  background-color: #f3f3f3;\n  display: flex;\n  flex-direction: column;\n\n  .card-header {\n    border-radius: 0.75rem 0.75rem 0 0;\n    margin: 0 !important;\n    background-color: #dfdfdf;\n    padding: 1.1rem;\n  }\n\n  .card-body {\n    display: flex;\n    flex-direction: column;\n    height: 100%;\n    justify-content: space-between;\n    color: #555;\n\n    .actions {\n      display: flex;\n      align-items: flex-end;\n      justify-content: center;\n      flex-wrap: wrap;\n      gap: 1rem;\n\n      & > div:last-of-type {\n        flex-basis: 100%;\n      }\n\n      & > form:last-of-type {\n        flex-basis: 100%;\n      }\n    }\n\n    .or-split {\n      text-align: center;\n      display: flex;\n      align-items: center;\n      justify-content: space-around;\n      width: 100%;\n\n      &::before,\n      &::after {\n        display: flex;\n        content: \"\";\n        width: 40%;\n        border-top: 1px solid #cccccc80;\n      }\n    }\n\n    .wrapper-form,\n    form {\n      border-radius: 0.35rem;\n      display: flex;\n      gap: 0.5rem;\n      flex-direction: column;\n      justify-content: end;\n      align-items: start;\n      width: calc(50% - 0.5rem);\n\n      button {\n        width: 100%;\n      }\n\n      .input-wrapper {\n        width: 100%;\n      }\n\n      input::placeholder {\n        color: #ccc;\n      }\n    }\n\n    .signInButton button,\n    .social-btn button {\n      padding: 0.5rem;\n      display: flex;\n      gap: 1rem;\n      justify-content: center;\n      align-items: center;\n    }\n\n    .signInButton button span {\n      display: flex;\n      gap: 1rem;\n      align-items: center;\n    }\n\n    &:has(button) {\n      padding: 1.1rem;\n    }\n  }\n\n  .card-footer {\n    padding: 1.1rem;\n    padding-top: 0;\n    color: #777;\n    font-style: italic;\n  }\n\n  pre {\n    background-color: #ccc;\n    padding: 1rem;\n    border-radius: 0.5rem;\n    word-break: break-all;\n    white-space: pre-wrap;\n  }\n\n  .btn-wrapper {\n    display: flex;\n    gap: 1rem;\n  }\n\n  button {\n    justify-self: end;\n    font-weight: 500;\n    border-radius: 0.25rem;\n    border: none;\n    font-weight: bold;\n    cursor: pointer;\n    font-size: 1rem;\n    line-height: 1.4rem;\n    padding: 0.7rem 0.8rem;\n    position: relative;\n    z-index: 10;\n    background-color: #bbb;\n    color: black;\n    text-decoration: none;\n    padding: 0.7rem 1.4rem;\n    transition: all 250ms;\n  }\n\n  button:hover {\n    background-color: #aaa;\n  }\n}\n\n.input-wrapper {\n  display: flex;\n  gap: 0.5rem;\n  justify-content: start;\n  align-items: center;\n\n  label {\n    width: 8rem;\n    font-size: 1.1rem;\n  }\n\n  input {\n    border-radius: 0.25rem;\n    padding: 0.5rem;\n    border: #ccc solid 1px;\n    font-size: 1.05rem;\n    width: 100%;\n  }\n}\n\n/* Header */\nheader {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  border-radius: 0 0 0.6rem 0.6rem;\n  margin-bottom: 2rem;\n\n  nav {\n    width: 100%;\n    background-color: #f3f3f3;\n    display: flex;\n    padding: 1rem 0;\n    justify-content: space-between;\n    align-items: center;\n\n    button {\n      border: 0;\n    }\n\n    .nav-left {\n      display: flex;\n      padding: 0 1rem;\n    }\n\n    .nav-right {\n      display: flex;\n      padding: 0 1rem;\n      justify-content: end;\n      align-items: center;\n    }\n  }\n\n  .links {\n    display: flex;\n    padding: 0;\n    margin: 0;\n    list-style: none;\n    display: flex;\n    width: 100%;\n    gap: 0.5rem;\n    flex-wrap: wrap;\n\n    .linkItem {\n      flex-grow: 1;\n      display: inline-block;\n      text-align: center;\n      background-color: #d5d5d5;\n      border-radius: 0 0 0.5rem 0.5rem;\n      padding: 1rem 0 1rem 0;\n      text-decoration: none;\n      color: black;\n      transition: all 250ms;\n    }\n\n    .linkItem:hover {\n      background-color: #ccc;\n    }\n  }\n\n  .header-text {\n    font-size: 1.05rem;\n    margin-right: 1rem;\n  }\n\n  .avatar {\n    border-radius: 2rem;\n    float: left;\n    height: 2.8rem;\n    width: 2.8rem;\n    margin-right: 1rem;\n    background-color: white;\n    background-size: cover;\n    background-repeat: no-repeat;\n  }\n\n  .button,\n  .buttonPrimary {\n    font-weight: 500;\n    border-radius: 0.3rem;\n    font-weight: bold;\n    cursor: pointer;\n    font-size: 1rem;\n    line-height: 1.4rem;\n    padding: 0.7rem 0.8rem;\n    position: relative;\n    background-color: transparent;\n    color: #555;\n  }\n\n  .buttonPrimary {\n    background-color: #346df1;\n    border-color: #346df1;\n    color: #fff;\n    text-decoration: none;\n    padding: 0.7rem 1.4rem;\n  }\n\n  .buttonPrimary:hover {\n    box-shadow: inset 0 0 5rem rgba(0, 0, 0, 0.2);\n  }\n\n  @media screen and (max-width: 960px) {\n    padding: 0 1rem;\n  }\n}\n"
  },
  {
    "path": "apps/dev/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://kit.svelte.dev/docs/integrations#preprocessors\n  // for more information about preprocessors\n  preprocess: vitePreprocess(),\n\n  kit: {\n    adapter: adapter(),\n    alias: {\n      $: \"src\",\n      $components: \"src/components\",\n      $lib: \"src/lib\",\n    },\n  },\n}\n\nexport default config\n"
  },
  {
    "path": "apps/dev/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  }\n  // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias\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": "apps/dev/sveltekit/vite.config.js",
    "content": "import { defineConfig } from \"vite\"\nimport { sveltekit } from \"@sveltejs/kit/vite\"\n\nexport default defineConfig({\n  server: {\n    port: 3000,\n  },\n  plugins: [sveltekit()],\n})\n"
  },
  {
    "path": "apps/examples/express/.eslintrc.cjs",
    "content": "module.exports = {\n  root: true,\n  env: {\n    browser: true,\n    es2021: true,\n  },\n  extends: [\"eslint:recommended\", \"plugin:@typescript-eslint/recommended\"],\n  overrides: [\n    {\n      env: {\n        node: true,\n      },\n      files: [\".eslintrc.{js,cjs}\"],\n      parserOptions: {\n        sourceType: \"script\",\n      },\n    },\n  ],\n  parser: \"@typescript-eslint/parser\",\n  parserOptions: {\n    ecmaVersion: \"latest\",\n    sourceType: \"module\",\n  },\n  plugins: [\"@typescript-eslint\"],\n  rules: {},\n}\n"
  },
  {
    "path": "apps/examples/express/.gitignore",
    "content": "# API keys and secrets\n.env\n\n# Dependency directory\nnode_modules\n\n# Editors\n.idea\n*.iml\n.vscode/settings.json\n\n# OS metadata\n.DS_Store\nThumbs.db\n\n# Ignore built ts files\ndist/**/*\n\n# Ignore built css files\n/public/css/output.css\n\n"
  },
  {
    "path": "apps/examples/express/.prettierignore",
    "content": "\n.DS_Store\nnode_modules\n/dist\n/.turbo\n/package\n.env\n.env.*\n!.env.example\n\n# Ignore files for PNPM, NPM and YARN\npnpm-lock.yaml\npackage-lock.json\nyarn.lock\n"
  },
  {
    "path": "apps/examples/express/.prettierrc",
    "content": "{\n  \"semi\": false,\n  \"plugins\": [\"@prettier/plugin-pug\", \"prettier-plugin-tailwindcss\"],\n  \"pugClassNotation\": \"attribute\"\n}\n"
  },
  {
    "path": "apps/examples/express/README.md",
    "content": "> The example repository is maintained from a [monorepo](https://github.com/nextauthjs/next-auth/tree/main/apps/examples/express). Pull Requests should be opened against [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth).\n\n<p align=\"center\">\n   <br/>\n   <a href=\"https://authjs.dev\" target=\"_blank\">\n   <img height=\"64\" src=\"https://authjs.dev/img/logo-sm.png\" />\n   </a>\n   <a href=\"https://expressjs.com\" target=\"_blank\">\n   <img height=\"64\" src=\"https://i.cloudup.com/zfY6lL7eFa-3000x3000.png\" />\n   </a>\n   <h3 align=\"center\"><b>Express Auth</b> - Example App</h3>\n   <p align=\"center\">\n   Open Source. Full Stack. Own Your Data.\n   </p>\n   <p align=\"center\" style=\"align: center;\">\n      <a href=\"https://npm.im/@auth/express\">\n        <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/express?color=green&label=@auth/express&style=flat-square\">\n      </a>\n      <a href=\"https://bundlephobia.com/result?p=@auth/express\">\n        <img src=\"https://img.shields.io/bundlephobia/minzip/@auth/express?label=size&style=flat-square\" alt=\"Bundle Size\"/>\n      </a>\n      <a href=\"https://www.npmtrends.com/@auth/express\">\n        <img src=\"https://img.shields.io/npm/dm/@auth/express?label=downloads&style=flat-square\" alt=\"Downloads\" />\n      </a>\n      <a href=\"https://npm.im/@auth/express\">\n        <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n      </a>\n   </p>\n</p>\n\n## Overview\n\nThis is the official Express Auth example for [Auth.js](https://express.authjs.dev).\n\n## Getting started\n\nYou can instantly deploy this example to [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=express-auth-example) by clicking the following button.\n\n[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/nextauthjs/express-auth-example&project-name=express-auth-example&repository-name=express-auth-example)\n\n## Environment Variables\n\nOnce deployed, kindly ensure you set all [required environment variables](https://authjs.dev/getting-started/deployment#environment-variables) in the `Environment` section of your hosting service.\n\n## Node.js Compatibility\n\nThe recommended version of Node.js to use in this example is Node.js v20.0.0.\n\nIf you are using a version of Node.js lower than this (for example the minimum supported node version v18.0.0), you may need to enable Web Crypto API via the `--experimental-webcrypto` flag in the `start` and `dev` scripts of your `package.json` file.\n\nInstead of using the experimental flag, you may use the following polyfill:\n\n```ts\n// polyfill.cjs\nglobalThis.crypto ??= require(\"crypto\").webcrypto\n```\n\nAnd then import it within a top-level file in the application:\n\n```ts\n// server.ts\nimport \"./polyfill.cjs\"\n```\n"
  },
  {
    "path": "apps/examples/express/api/index.js",
    "content": "const { app } = await import(\"../src/app.js\")\n\nexport default app\n"
  },
  {
    "path": "apps/examples/express/package.json",
    "content": "{\n  \"description\": \"Express Auth example app\",\n  \"engines\": {\n    \"node\": \">=20.11.0\"\n  },\n  \"type\": \"module\",\n  \"private\": true,\n  \"scripts\": {\n    \"start\": \"node dist/server.js\",\n    \"clean\": \"rm -rf dist\",\n    \"build\": \"pnpm build:ts && pnpm build:css\",\n    \"build:ts\": \"tsc\",\n    \"build:css\": \"tailwindcss -i ./public/css/style.css -o ./public/css/output.css\",\n    \"dev\": \"tsx watch --env-file=.env src/server.ts & pnpm build:css -w\",\n    \"lint\": \"eslint src/*.ts --fix\",\n    \"prettier\": \"prettier src/*.ts --write\"\n  },\n  \"author\": \"Rexford Essilfie <rexfordessilfie09@gmail.com>\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"@auth/express\": \"latest\",\n    \"express\": \"^4.19.2\",\n    \"morgan\": \"^1.10.0\",\n    \"pug\": \"^3.0.2\",\n    \"tailwindcss\": \"^3.4.3\"\n  },\n  \"devDependencies\": {\n    \"@types/express\": \"^4.17.21\",\n    \"@types/morgan\": \"^1.9.9\",\n    \"@types/node\": \"^20.12.7\",\n    \"@types/pug\": \"^2.0.10\",\n    \"tsx\": \"^4.7.0\",\n    \"typescript\": \"5.3.3\"\n  }\n}\n"
  },
  {
    "path": "apps/examples/express/public/css/style.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n"
  },
  {
    "path": "apps/examples/express/src/app.ts",
    "content": "// @ts-nocheck\nimport express, { type Request, type Response } from \"express\"\nimport logger from \"morgan\"\nimport * as path from \"node:path\"\nimport {\n  errorHandler,\n  errorNotFoundHandler,\n} from \"./middleware/error.middleware.js\"\nimport {\n  authenticatedUser,\n  currentSession,\n} from \"./middleware/auth.middleware.js\"\n\nimport { ExpressAuth } from \"@auth/express\"\nimport { authConfig } from \"./config/auth.config.js\"\nimport * as pug from \"pug\"\n\nexport const app = express()\n\napp.set(\"port\", process.env.PORT || 3000)\n\n// @ts-expect-error (https://stackoverflow.com/questions/45342307/error-cannot-find-module-pug)\napp.engine(\"pug\", pug.__express)\napp.set(\"views\", path.join(import.meta.dirname, \"..\", \"views\"))\napp.set(\"view engine\", \"pug\")\n\n// Trust Proxy for Proxies (Heroku, Render.com, Docker behind Nginx, etc)\n// https://stackoverflow.com/questions/40459511/in-express-js-req-protocol-is-not-picking-up-https-for-my-secure-link-it-alwa\napp.set(\"trust proxy\", true)\n\napp.use(logger(\"dev\"))\n\n// Serve static files\n// NB: Uncomment this out if you want Express to serve static files for you vs. using a\n// hosting provider which does so for you (for example through a CDN).\napp.use(express.static(path.join(import.meta.dirname, \"..\", \"public\")))\n\n// Parse incoming requests data\napp.use(express.urlencoded({ extended: true }))\napp.use(express.json())\n\n// Set session in res.locals\napp.use(currentSession)\n\n// Set up ExpressAuth to handle authentication\n// IMPORTANT: It is highly encouraged set up rate limiting on this route\napp.use(\"/api/auth/*\", ExpressAuth(authConfig))\n\n// Routes\napp.get(\"/protected\", async (_req: Request, res: Response) => {\n  res.render(\"protected\", { session: res.locals.session })\n})\n\napp.get(\n  \"/api/protected\",\n  authenticatedUser,\n  async (_req: Request, res: Response) => {\n    res.json(res.locals.session)\n  },\n)\n\napp.get(\"/\", async (_req: Request, res: Response) => {\n  res.render(\"index\", {\n    title: \"Express Auth Example\",\n    user: res.locals.session?.user,\n  })\n})\n\n// Error handlers\napp.use(errorNotFoundHandler)\napp.use(errorHandler)\n"
  },
  {
    "path": "apps/examples/express/src/config/auth.config.ts",
    "content": "import Apple from \"@auth/express/providers/apple\"\nimport Auth0 from \"@auth/express/providers/auth0\"\nimport AzureB2C from \"@auth/express/providers/azure-ad-b2c\"\nimport BoxyHQSAML from \"@auth/express/providers/boxyhq-saml\"\nimport Cognito from \"@auth/express/providers/cognito\"\nimport Coinbase from \"@auth/express/providers/coinbase\"\nimport Discord from \"@auth/express/providers/discord\"\nimport Dropbox from \"@auth/express/providers/dropbox\"\nimport Facebook from \"@auth/express/providers/facebook\"\nimport GitHub from \"@auth/express/providers/github\"\nimport GitLab from \"@auth/express/providers/gitlab\"\nimport Google from \"@auth/express/providers/google\"\nimport Hubspot from \"@auth/express/providers/hubspot\"\nimport Keycloak from \"@auth/express/providers/keycloak\"\nimport LinkedIn from \"@auth/express/providers/linkedin\"\nimport Netlify from \"@auth/express/providers/netlify\"\nimport Okta from \"@auth/express/providers/okta\"\nimport Passage from \"@auth/express/providers/passage\"\nimport Pinterest from \"@auth/express/providers/pinterest\"\nimport Reddit from \"@auth/express/providers/reddit\"\nimport Slack from \"@auth/express/providers/slack\"\nimport Spotify from \"@auth/express/providers/spotify\"\nimport Twitch from \"@auth/express/providers/twitch\"\nimport Twitter from \"@auth/express/providers/twitter\"\nimport WorkOS from \"@auth/express/providers/workos\"\nimport Zoom from \"@auth/express/providers/zoom\"\n\nexport const authConfig = {\n  trustHost: true,\n  providers: [\n    Apple,\n    Auth0,\n    AzureB2C({\n      clientId: process.env.AUTH_AZURE_AD_B2C_ID,\n      clientSecret: process.env.AUTH_AZURE_AD_B2C_SECRET,\n      issuer: process.env.AUTH_AZURE_AD_B2C_ISSUER,\n    }),\n    BoxyHQSAML({\n      clientId: \"dummy\",\n      clientSecret: \"dummy\",\n      issuer: process.env.AUTH_BOXYHQ_SAML_ISSUER,\n    }),\n    Cognito,\n    Coinbase,\n    Discord,\n    Dropbox,\n    Facebook,\n    GitHub,\n    GitLab,\n    Google,\n    Hubspot,\n    Keycloak,\n    LinkedIn,\n    Netlify,\n    Okta,\n    Passage,\n    Pinterest,\n    Reddit,\n    Slack,\n    Spotify,\n    Twitch,\n    Twitter,\n    WorkOS({\n      connection: process.env.AUTH_WORKOS_CONNECTION!,\n    }),\n    Zoom,\n  ],\n}\n"
  },
  {
    "path": "apps/examples/express/src/errors.ts",
    "content": "export class HttpError extends Error {\n  status: number\n  constructor(status: number, message: string) {\n    super(message)\n    this.status = status\n  }\n}\n\nexport class NotFoundError extends HttpError {\n  constructor(message: string, status = 404) {\n    super(status, message)\n    this.name = \"NotFoundError\"\n  }\n}\n"
  },
  {
    "path": "apps/examples/express/src/middleware/auth.middleware.ts",
    "content": "// @ts-nocheck\nimport { getSession } from \"@auth/express\"\nimport { authConfig } from \"../config/auth.config.js\"\nimport type { NextFunction, Request, Response } from \"express\"\n\nexport async function authenticatedUser(\n  req: Request,\n  res: Response,\n  next: NextFunction,\n) {\n  const session =\n    res.locals.session ?? (await getSession(req, authConfig)) ?? undefined\n\n  res.locals.session = session\n\n  if (session) {\n    return next()\n  }\n\n  res.status(401).json({ message: \"Not Authenticated\" })\n}\n\nexport async function currentSession(\n  req: Request,\n  res: Response,\n  next: NextFunction,\n) {\n  const session = (await getSession(req, authConfig)) ?? undefined\n  res.locals.session = session\n  return next()\n}\n"
  },
  {
    "path": "apps/examples/express/src/middleware/error.middleware.ts",
    "content": "// @ts-nocheck\nimport type { NextFunction, Request, Response } from \"express\"\nimport { HttpError, NotFoundError } from \"../errors.js\"\n\nexport const errorHandler = (\n  err: HttpError | Error,\n  _req: Request,\n  res: Response,\n  _next: NextFunction,\n): void => {\n  // Render the error page\n  res.status((\"status\" in err && err.status) || 500)\n  res.render(\"error\", {\n    title: \"status\" in err ? err.status : err.name,\n    message: err.message,\n  })\n}\n\nexport const errorNotFoundHandler = (\n  _req: Request,\n  _res: Response,\n  next: NextFunction,\n): void => {\n  next(new NotFoundError(\"Not Found\"))\n}\n"
  },
  {
    "path": "apps/examples/express/src/server.ts",
    "content": "const { app } = await import(\"./app.js\")\n\nconst port = app.get(\"port\")\n\nconst server = app.listen(port, () => {\n  console.log(`Listening on port ${port}`)\n})\n\nexport default server\n"
  },
  {
    "path": "apps/examples/express/tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nexport default {\n  content: [\"./src/*.{html,js,css}\", \"./views/*.pug\"],\n  theme: {\n    extend: {},\n  },\n  plugins: [],\n}\n"
  },
  {
    "path": "apps/examples/express/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"NodeNext\",\n    \"esModuleInterop\": true,\n    \"target\": \"esnext\",\n    \"noImplicitAny\": true,\n    \"moduleResolution\": \"NodeNext\",\n    \"sourceMap\": true,\n    \"outDir\": \"dist\",\n    \"baseUrl\": \".\",\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"typeRoots\": [\"types/\", \"node_modules/@types\"]\n  },\n  \"include\": [\"src/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "apps/examples/express/types/express/index.d.ts",
    "content": "import { type Session } from \"@auth/express\"\n\ndeclare module \"express\" {\n  interface Response {\n    locals: {\n      session?: Session\n    }\n  }\n}\n"
  },
  {
    "path": "apps/examples/express/vercel.json",
    "content": "{\n  \"$schema\": \"https://openapi.vercel.sh/vercel.json\",\n  \"rewrites\": [{ \"source\": \"/(.*)\", \"destination\": \"/api\" }]\n}\n"
  },
  {
    "path": "apps/examples/express/views/error.pug",
    "content": "extends layout\n\nblock content\n  h1(class=\"text-3xl font-bold\")= title\n  p= message\n"
  },
  {
    "path": "apps/examples/express/views/index.pug",
    "content": "extends layout\n\nblock content\n  h1(class=\"text-3xl font-bold\")= title\n  p\n    | This is an example site to demonstrate how to use #{ ' ' }\n    a(href=\"https://expressjs.com/\", class=\"mb-2 font-medium underline\") Express\n    | #{ ' ' } with #{ ' ' }\n    a(href=\"https://authjs.dev/reference/express\", class=\"underline\") Express Auth\n    |\n    | for authentication.\n"
  },
  {
    "path": "apps/examples/express/views/layout.pug",
    "content": "doctype html\nhtml\n  head\n    title= title\n    link(rel=\"stylesheet\", href=\"/css/output.css\")\n    meta(name=\"viewport\", content=\"width=device-width, initial-scale=1.0\")\n  body(class=\"flex flex-col items-center\")\n    div(class=\"flex w-[90%] max-w-2xl flex-col justify-start gap-2\")\n      div(\n        class=\"mb-2 flex items-center justify-between gap-2 rounded-b-md bg-gray-100 p-2 pl-4\"\n      )\n        if session\n          div(class=\"flex flex-row items-center gap-2\")\n            if session.user.image\n              img(src=`${session.user.image}`, class=\"h-8 w-8 rounded-full\")\n            span\n              | Signed in as #{ ' ' }\n              strong= session.user.email || session.user.name\n          a(\n            class=\"rounded-md bg-blue-600 px-5 py-2.5 font-medium text-white\",\n            href=\"/api/auth/signout\"\n          ) Sign out\n        else\n          span(class=\"\") You are not signed in\n          a#sign-indiv(\n            ,\n            class=\"rounded-md bg-blue-600 px-5 py-2.5 font-medium text-white\",\n            href=\"/api/auth/signin\"\n          ) Sign in\n\n      nav(class=\"mb-4\")\n        ul(class=\"flex flex-row gap-4 underline\")\n          li\n            a(href=\"/\") Home\n          li\n            a(href=\"/protected\") Protected\n          li\n            a(href=\"/api/protected\") Protected (API)\n\n      block content\n"
  },
  {
    "path": "apps/examples/express/views/protected.pug",
    "content": "extends layout\n\nblock content\n  if session\n    h1(class=\"mb-2 text-3xl font-medium\") Protected page\n    p\n      | This is a protected content. You can access this content because you are\n      | signed in.\n    p Session expiry: #{ session.expires ? session.expires : '' }\n  else\n    h1(class=\"mb-2 text-3xl font-medium\") Access Denied\n    p\n      | You must be #{ ' ' }\n      a(href=\"/api/auth/signin\", class=\"underline\") signed in\n      | #{ ' ' } to view this page\n"
  },
  {
    "path": "apps/examples/nextjs/.gitignore",
    "content": ".DS_Store\n\nnode_modules/\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.yarn-integrity\n.npm\n\n.eslintcache\n\n*.tsbuildinfo\nnext-env.d.ts\n\n.next\n.vercel\n.env*.local"
  },
  {
    "path": "apps/examples/nextjs/Dockerfile",
    "content": "# syntax=docker/dockerfile:1\nFROM node:20-alpine AS base\n\n# Install dependencies only when needed\nFROM base AS deps\n# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.\nRUN apk add --no-cache libc6-compat\nWORKDIR /app\n\n# Install dependencies\nCOPY package.json pnpm-lock.yaml* ./\nRUN corepack enable pnpm && pnpm i --frozen-lockfile\n\n# Rebuild the source code only when needed\nFROM base AS builder\nWORKDIR /app\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY . .\n\n# Next.js collects completely anonymous telemetry data about general usage.\n# Learn more here: https://nextjs.org/telemetry\n# Uncomment the following line in case you want to disable telemetry during the build.\n# ENV NEXT_TELEMETRY_DISABLED 1\n\nRUN corepack enable pnpm && pnpm build\n\n# Production image, copy all the files and run next\nFROM base AS runner\nWORKDIR /app\n\nENV NODE_ENV production\n# Uncomment the following line in case you want to disable telemetry during runtime.\n# ENV NEXT_TELEMETRY_DISABLED 1\n\nRUN addgroup --system --gid 1001 nodejs\nRUN adduser --system --uid 1001 nextjs\n\nCOPY --from=builder /app/public ./public\n\n# Set the correct permission for prerender cache\nRUN mkdir .next\nRUN chown nextjs:nodejs .next\n\n# Automatically leverage output traces to reduce image size\n# https://nextjs.org/docs/advanced-features/output-file-tracing\nCOPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./\nCOPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static\n\nUSER nextjs\n\nEXPOSE 3000\n\nENV PORT 3000\nENV HOSTNAME \"0.0.0.0\"\n\n# server.js is created by next build from the standalone output\n# https://nextjs.org/docs/pages/api-reference/next-config-js/output\nCMD [\"node\", \"server.js\"]\n"
  },
  {
    "path": "apps/examples/nextjs/README.md",
    "content": "> The example repository is maintained from a [monorepo](https://github.com/nextauthjs/next-auth/tree/main/apps/examples/nextjs). Pull Requests should be opened against [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth).\n\n<p align=\"center\">\n   <br/>\n   <a href=\"https://authjs.dev\" target=\"_blank\"><img width=\"150px\" src=\"https://authjs.dev/img/logo-sm.png\" /></a>\n   <h3 align=\"center\">NextAuth.js Example App</h3>\n   <p align=\"center\">\n   Open Source. Full Stack. Own Your Data.\n   </p>\n   <p align=\"center\" style=\"align: center;\">\n      <a href=\"https://npm.im/next-auth\">\n        <img alt=\"npm\" src=\"https://img.shields.io/npm/v/next-auth?color=green&label=next-auth\">\n      </a>\n      <a href=\"https://bundlephobia.com/result?p=next-auth-example\">\n        <img src=\"https://img.shields.io/bundlephobia/minzip/next-auth?label=next-auth\" alt=\"Bundle Size\"/>\n      </a>\n      <a href=\"https://www.npmtrends.com/next-auth\">\n        <img src=\"https://img.shields.io/npm/dm/next-auth?label=next-auth%20downloads\" alt=\"Downloads\" />\n      </a>\n      <a href=\"https://npm.im/next-auth\">\n        <img src=\"https://img.shields.io/badge/npm-TypeScript-blue\" alt=\"TypeScript\" />\n      </a>\n   </p>\n</p>\n\n## Overview\n\nNextAuth.js is a complete open source authentication solution.\n\nThis is an example application that shows how `next-auth` is applied to a basic Next.js app.\n\nThe deployed version can be found at [`next-auth-example.vercel.app`](https://next-auth-example.vercel.app)\n\n### About NextAuth.js\n\nNextAuth.js is an easy to implement, full-stack (client/server) open source authentication library originally designed for [Next.js](https://nextjs.org) and [Serverless](https://vercel.com). Our goal is to [support even more frameworks](https://github.com/nextauthjs/next-auth/issues/2294) in the future.\n\nGo to [next-auth.js.org](https://authjs.dev) for more information and documentation.\n\n> _NextAuth.js is not officially associated with Vercel or Next.js._\n\n## Getting Started\n\n### 1. Clone the repository and install dependencies\n\n```\ngit clone https://github.com/nextauthjs/next-auth-example.git\ncd next-auth-example\npnpm install\n```\n\n### 2. Configure your local environment\n\nCopy the .env.local.example file in this directory to .env.local (which will be ignored by Git):\n\n```\ncp .env.local.example .env.local\n```\n\nAdd details for one or more providers (e.g. Google, Twitter, GitHub, Email, etc).\n\n#### Database\n\nA database is needed to persist user accounts and to support email sign in. However, you can still use NextAuth.js for authentication without a database by using OAuth for authentication. If you do not specify a database, [JSON Web Tokens](https://jwt.io/introduction) will be enabled by default.\n\nYou **can** skip configuring a database and come back to it later if you want.\n\nFor more information about setting up a database, please check out the following links:\n\n- Docs: [authjs.dev/reference/core/adapters](https://authjs.dev/reference/core/adapters)\n\n### 3. Configure Authentication Providers\n\n1. Review and update options in `auth.ts` as needed.\n\n2. When setting up OAuth, in the developer admin page for each of your OAuth services, you should configure the callback URL to use a callback path of `{server}/api/auth/callback/{provider}`.\n\ne.g. For Google OAuth you would use: `http://localhost:3000/api/auth/callback/google`\n\nA list of configured providers and their callback URLs is available from the endpoint `api/auth/providers`. You can find more information at https://authjs.dev/getting-started/providers/oauth-tutorial\n\n1. You can also choose to specify an SMTP server for passwordless sign in via email.\n\n### 4. Start the application\n\nTo run your site locally, use:\n\n```\npnpm run dev\n```\n\nTo run it in production mode, use:\n\n```\npnpm run build\npnpm run start\n```\n\n### 5. Preparing for Production\n\nFollow the [Deployment documentation](https://authjs.dev/getting-started/deployment)\n\n## License\n\nISC\n"
  },
  {
    "path": "apps/examples/nextjs/app/[...proxy]/route.tsx",
    "content": "import { auth } from \"@/auth\"\nimport { NextRequest } from \"next/server\"\n\n// Review if we need this, and why\nfunction stripContentEncoding(result: Response) {\n  const responseHeaders = new Headers(result.headers)\n  responseHeaders.delete(\"content-encoding\")\n\n  return new Response(result.body, {\n    status: result.status,\n    statusText: result.statusText,\n    headers: responseHeaders,\n  })\n}\n\nasync function handler(request: NextRequest) {\n  const session = await auth()\n\n  const headers = new Headers(request.headers)\n  headers.set(\"Authorization\", `Bearer ${session?.accessToken}`)\n\n  let backendUrl =\n    process.env.THIRD_PARTY_API_EXAMPLE_BACKEND ??\n    \"https://third-party-backend.authjs.dev\"\n\n  let url = request.nextUrl.href.replace(request.nextUrl.origin, backendUrl)\n  let result = await fetch(url, { headers, body: request.body })\n\n  return stripContentEncoding(result)\n}\n\nexport const dynamic = \"force-dynamic\"\n\nexport { handler as GET, handler as POST }\n"
  },
  {
    "path": "apps/examples/nextjs/app/api/protected/route.ts",
    "content": "import { auth } from \"auth\"\n\nexport const GET = auth((req) => {\n  if (req.auth) {\n    return Response.json({ data: \"Protected data\" })\n  }\n\n  return Response.json({ message: \"Not authenticated\" }, { status: 401 })\n})\n"
  },
  {
    "path": "apps/examples/nextjs/app/api-example/page.tsx",
    "content": "\"use client\"\nimport CustomLink from \"@/components/custom-link\"\nimport { useEffect, useState } from \"react\"\n\nexport default function Page() {\n  const [data, setData] = useState()\n  useEffect(() => {\n    ;(async () => {\n      const res = await fetch(\"/api/protected\")\n      const json = await res.json()\n      setData(json)\n    })()\n  }, [])\n  return (\n    <div className=\"flex flex-col gap-6\">\n      <h1 className=\"text-3xl font-bold\">Route Handler Usage</h1>\n      <p>\n        This page fetches data from an API{\" \"}\n        <CustomLink href=\"https://nextjs.org/docs/app/building-your-application/routing/route-handlers\">\n          Route Handler\n        </CustomLink>\n        . The API is protected using the universal{\" \"}\n        <CustomLink href=\"https://nextjs.authjs.dev#auth\">\n          <code>auth()</code>\n        </CustomLink>{\" \"}\n        method.\n      </p>\n      <div className=\"flex flex-col rounded-md bg-gray-100\">\n        <div className=\"rounded-t-md bg-gray-200 p-4 font-bold\">\n          Data from API Route\n        </div>\n        <pre className=\"whitespace-pre-wrap break-all px-4 py-6\">\n          {JSON.stringify(data, null, 2)}\n        </pre>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs/app/auth/[...nextauth]/route.ts",
    "content": "import { handlers } from \"auth\"\nexport const { GET, POST } = handlers\n"
  },
  {
    "path": "apps/examples/nextjs/app/client-example/page.tsx",
    "content": "import { auth } from \"auth\"\nimport ClientExample from \"@/components/client-example\"\nimport { SessionProvider } from \"next-auth/react\"\n\nexport default async function ClientPage() {\n  const session = await auth()\n  if (session?.user) {\n    // TODO: Look into https://react.dev/reference/react/experimental_taintObjectReference\n    // filter out sensitive data before passing to client.\n    session.user = {\n      name: session.user.name,\n      email: session.user.email,\n      image: session.user.image,\n    }\n  }\n\n  return (\n    <SessionProvider basePath={\"/auth\"} session={session}>\n      <ClientExample />\n    </SessionProvider>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs/app/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --foreground: 222.2 84% 4.9%;\n\n    --card: 0 0% 100%;\n    --card-foreground: 222.2 84% 4.9%;\n\n    --popover: 0 0% 100%;\n    --popover-foreground: 222.2 84% 4.9%;\n\n    --primary: 222.2 47.4% 11.2%;\n    --primary-foreground: 210 40% 98%;\n\n    --secondary: 210 40% 96.1%;\n    --secondary-foreground: 222.2 47.4% 11.2%;\n\n    --muted: 210 40% 96.1%;\n    --muted-foreground: 215.4 16.3% 46.9%;\n\n    --accent: 210 40% 96.1%;\n    --accent-foreground: 222.2 47.4% 11.2%;\n\n    --destructive: 0 84.2% 60.2%;\n    --destructive-foreground: 210 40% 98%;\n\n    --border: 214.3 31.8% 91.4%;\n    --input: 214.3 31.8% 91.4%;\n    --ring: 222.2 84% 4.9%;\n\n    --radius: 0.5rem;\n  }\n}\n\n@layer base {\n  * {\n    @apply border-border;\n  }\n\n  body {\n    @apply bg-background text-foreground;\n  }\n}\n"
  },
  {
    "path": "apps/examples/nextjs/app/layout.tsx",
    "content": "import \"./globals.css\"\nimport type { Metadata } from \"next\"\nimport { Inter } from \"next/font/google\"\nimport Footer from \"@/components/footer\"\nimport Header from \"@/components/header\"\n\nconst inter = Inter({ subsets: [\"latin\"] })\n\nexport const metadata: Metadata = {\n  title: \"NextAuth.js Example\",\n  description:\n    \"This is an example site to demonstrate how to use NextAuth.js for authentication\",\n}\n\nexport default function RootLayout({ children }: React.PropsWithChildren) {\n  return (\n    <html lang=\"en\">\n      <body className={inter.className}>\n        <div className=\"flex h-full min-h-screen w-full flex-col justify-between\">\n          <Header />\n          <main className=\"mx-auto w-full max-w-3xl flex-auto px-4 py-4 sm:px-6 md:py-6\">\n            {children}\n          </main>\n          <Footer />\n        </div>\n      </body>\n    </html>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs/app/middleware-example/page.tsx",
    "content": "import CustomLink from \"@/components/custom-link\"\n\nexport default function Page() {\n  return (\n    <div className=\"space-y-2\">\n      <h1 className=\"text-3xl font-bold\">Middleware usage</h1>\n      <p>\n        This page is protected by using the universal{\" \"}\n        <CustomLink href=\"https://nextjs.authjs.dev#auth\">\n          <code>auth()</code>\n        </CustomLink>{\" \"}\n        method in{\" \"}\n        <CustomLink href=\"https://nextjs.org/docs/app/building-your-application/routing/middleware\">\n          Next.js Middleware\n        </CustomLink>\n        .\n      </p>\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs/app/page.tsx",
    "content": "import CustomLink from \"@/components/custom-link\"\nimport { auth } from \"auth\"\n\nexport default async function Index() {\n  const session = await auth()\n\n  return (\n    <div className=\"flex flex-col gap-6\">\n      <h1 className=\"text-3xl font-bold\">NextAuth.js Example</h1>\n      <div>\n        This is an example site to demonstrate how to use{\" \"}\n        <CustomLink href=\"https://nextjs.authjs.dev\">NextAuth.js</CustomLink>{\" \"}\n        for authentication. Check out the{\" \"}\n        <CustomLink href=\"/server-example\" className=\"underline\">\n          Server\n        </CustomLink>{\" \"}\n        and the{\" \"}\n        <CustomLink href=\"/client-example\" className=\"underline\">\n          Client\n        </CustomLink>{\" \"}\n        examples to see how to secure pages and get session data.\n      </div>\n      <div>\n        WebAuthn users are reset on every deploy, don't expect your test user(s)\n        to still be available after a few days. It is designed to only\n        demonstrate registration, login, and logout briefly.\n      </div>\n      <div className=\"flex flex-col rounded-md bg-gray-100\">\n        <div className=\"rounded-t-md bg-gray-200 p-4 font-bold\">\n          Current Session\n        </div>\n        <pre className=\"whitespace-pre-wrap break-all px-4 py-6\">\n          {JSON.stringify(session, null, 2)}\n        </pre>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs/app/policy/page.tsx",
    "content": "export default function PolicyPage() {\n  return (\n    <div className=\"space-y-2\">\n      <section>\n        <h2 className=\"text-xl font-bold\">Terms of Service</h2>\n        <p>\n          THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n          EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n          MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n          IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n          CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n          TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n          SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n        </p>\n      </section>\n      <section>\n        <h2 className=\"text-xl font-bold\">Privacy Policy</h2>\n        <p>\n          This site uses JSON Web Tokens and a Key-Value database for sessions\n          and WebAuthn authenticators which resets every 2 hours.\n        </p>\n        <p>\n          Data provided to this site is exclusively used to support signing in\n          and is not passed to any third party services, other than via SMTP or\n          OAuth for the purposes of authentication. And Vercel KV / Upstash for\n          hosting the Key Value store. This data is deleted every 2 hours via\n          cron job.\n        </p>\n      </section>\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs/app/server-example/page.tsx",
    "content": "import CustomLink from \"@/components/custom-link\"\nimport SessionData from \"@/components/session-data\"\nimport { auth } from \"auth\"\n\nexport default async function Page() {\n  const session = await auth()\n  return (\n    <div className=\"space-y-2\">\n      <h1 className=\"text-3xl font-bold\">React Server Component Usage</h1>\n      <p>\n        This page is server-rendered as a{\" \"}\n        <CustomLink href=\"https://nextjs.org/docs/app/building-your-application/rendering/server-components\">\n          React Server Component\n        </CustomLink>\n        . It gets the session data on the server using{\" \"}\n        <CustomLink href=\"https://nextjs.authjs.dev#auth\">\n          <code>auth()</code>\n        </CustomLink>{\" \"}\n        method.\n      </p>\n      <SessionData session={session} />\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs/auth.ts",
    "content": "import NextAuth from \"next-auth\"\nimport \"next-auth/jwt\"\n\nimport Apple from \"next-auth/providers/apple\"\n// import Atlassian from \"next-auth/providers/atlassian\"\nimport Auth0 from \"next-auth/providers/auth0\"\nimport AzureB2C from \"next-auth/providers/azure-ad-b2c\"\nimport BankIDNorway from \"next-auth/providers/bankid-no\"\nimport BoxyHQSAML from \"next-auth/providers/boxyhq-saml\"\nimport Cognito from \"next-auth/providers/cognito\"\nimport Coinbase from \"next-auth/providers/coinbase\"\nimport Discord from \"next-auth/providers/discord\"\nimport Dropbox from \"next-auth/providers/dropbox\"\nimport Facebook from \"next-auth/providers/facebook\"\nimport GitHub from \"next-auth/providers/github\"\nimport GitLab from \"next-auth/providers/gitlab\"\nimport Google from \"next-auth/providers/google\"\nimport Hubspot from \"next-auth/providers/hubspot\"\nimport Keycloak from \"next-auth/providers/keycloak\"\nimport LinkedIn from \"next-auth/providers/linkedin\"\nimport MicrosoftEntraId from \"next-auth/providers/microsoft-entra-id\"\nimport Netlify from \"next-auth/providers/netlify\"\nimport Okta from \"next-auth/providers/okta\"\nimport Passage from \"next-auth/providers/passage\"\nimport Passkey from \"next-auth/providers/passkey\"\nimport Pinterest from \"next-auth/providers/pinterest\"\nimport Reddit from \"next-auth/providers/reddit\"\nimport Slack from \"next-auth/providers/slack\"\nimport Salesforce from \"next-auth/providers/salesforce\"\nimport Spotify from \"next-auth/providers/spotify\"\nimport Twitch from \"next-auth/providers/twitch\"\nimport Twitter from \"next-auth/providers/twitter\"\nimport Vipps from \"next-auth/providers/vipps\"\nimport WorkOS from \"next-auth/providers/workos\"\nimport Zoom from \"next-auth/providers/zoom\"\nimport { createStorage } from \"unstorage\"\nimport memoryDriver from \"unstorage/drivers/memory\"\nimport vercelKVDriver from \"unstorage/drivers/vercel-kv\"\nimport { UnstorageAdapter } from \"@auth/unstorage-adapter\"\n\nconst storage = createStorage({\n  driver: process.env.VERCEL\n    ? vercelKVDriver({\n        url: process.env.AUTH_KV_REST_API_URL,\n        token: process.env.AUTH_KV_REST_API_TOKEN,\n        env: false,\n      })\n    : memoryDriver(),\n})\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  debug: !!process.env.AUTH_DEBUG,\n  theme: { logo: \"https://authjs.dev/img/logo-sm.png\" },\n  adapter: UnstorageAdapter(storage),\n  providers: [\n    Apple,\n    // Atlassian,\n    Auth0,\n    AzureB2C,\n    BankIDNorway,\n    BoxyHQSAML({\n      clientId: \"dummy\",\n      clientSecret: \"dummy\",\n      issuer: process.env.AUTH_BOXYHQ_SAML_ISSUER,\n    }),\n    Cognito,\n    Coinbase,\n    Discord,\n    Dropbox,\n    Facebook,\n    GitHub,\n    GitLab,\n    Google,\n    Hubspot,\n    Keycloak({ name: \"Keycloak (bob/bob)\" }),\n    LinkedIn,\n    MicrosoftEntraId,\n    Netlify,\n    Okta,\n    Passkey({\n      formFields: {\n        email: {\n          label: \"Username\",\n          required: true,\n          autocomplete: \"username webauthn\",\n        },\n      },\n    }),\n    Passage,\n    Pinterest,\n    Reddit,\n    Salesforce,\n    Slack,\n    Spotify,\n    Twitch,\n    Twitter,\n    Vipps({\n      issuer: \"https://apitest.vipps.no/access-management-1.0/access/\",\n    }),\n    WorkOS({ connection: process.env.AUTH_WORKOS_CONNECTION! }),\n    Zoom,\n  ],\n  basePath: \"/auth\",\n  session: { strategy: \"jwt\" },\n  callbacks: {\n    authorized({ request, auth }) {\n      const { pathname } = request.nextUrl\n      if (pathname === \"/middleware-example\") return !!auth\n      return true\n    },\n    jwt({ token, trigger, session, account }) {\n      if (trigger === \"update\") token.name = session.user.name\n      if (account?.provider === \"keycloak\") {\n        return { ...token, accessToken: account.access_token }\n      }\n      return token\n    },\n    async session({ session, token }) {\n      if (token?.accessToken) session.accessToken = token.accessToken\n\n      return session\n    },\n  },\n  experimental: { enableWebAuthn: true },\n})\n\ndeclare module \"next-auth\" {\n  interface Session {\n    accessToken?: string\n  }\n}\n\ndeclare module \"next-auth/jwt\" {\n  interface JWT {\n    accessToken?: string\n  }\n}\n"
  },
  {
    "path": "apps/examples/nextjs/components/auth-components.tsx",
    "content": "import { signIn, signOut } from \"auth\"\nimport { Button } from \"./ui/button\"\n\nexport function SignIn({\n  provider,\n  ...props\n}: { provider?: string } & React.ComponentPropsWithRef<typeof Button>) {\n  return (\n    <form\n      action={async () => {\n        \"use server\"\n        await signIn(provider)\n      }}\n    >\n      <Button {...props}>Sign In</Button>\n    </form>\n  )\n}\n\nexport function SignOut(props: React.ComponentPropsWithRef<typeof Button>) {\n  return (\n    <form\n      action={async () => {\n        \"use server\"\n        await signOut()\n      }}\n      className=\"w-full\"\n    >\n      <Button variant=\"ghost\" className=\"w-full p-0\" {...props}>\n        Sign Out\n      </Button>\n    </form>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs/components/client-example.tsx",
    "content": "\"use client\"\n\nimport { useSession } from \"next-auth/react\"\nimport { Button } from \"./ui/button\"\nimport { Input } from \"./ui/input\"\nimport { useState } from \"react\"\nimport SessionData from \"./session-data\"\nimport CustomLink from \"./custom-link\"\n\nconst UpdateForm = () => {\n  const { data: session, update } = useSession()\n  const [name, setName] = useState(`New ${session?.user?.name}`)\n\n  if (!session?.user) return null\n  return (\n    <>\n      <h2 className=\"text-xl font-bold\">Updating the session client-side</h2>\n      <div className=\"flex w-full max-w-sm items-center space-x-2\">\n        <Input\n          type=\"text\"\n          placeholder=\"New name\"\n          value={name}\n          onChange={(e) => {\n            setName(e.target.value)\n          }}\n        />\n        <Button onClick={() => update({ user: { name } })} type=\"submit\">\n          Update\n        </Button>\n      </div>\n    </>\n  )\n}\n\nexport default function ClientExample() {\n  const { data: session, status } = useSession()\n  const [apiResponse, setApiResponse] = useState(\"\")\n\n  const makeRequestWithToken = async () => {\n    try {\n      const response = await fetch(\"/api/authenticated/greeting\")\n      const data = await response.json()\n      setApiResponse(JSON.stringify(data, null, 2))\n    } catch (error) {\n      setApiResponse(\"Failed to fetch data: \" + error)\n    }\n  }\n\n  return (\n    <div className=\"flex flex-col gap-4\">\n      <h1 className=\"text-3xl font-bold\">Client Side Rendering</h1>\n      <p>\n        This page fetches session data client side using the{\" \"}\n        <CustomLink href=\"https://nextjs.authjs.dev/react#usesession\">\n          <code>useSession</code>\n        </CustomLink>{\" \"}\n        React Hook.\n      </p>\n      <p>\n        It needs the{\" \"}\n        <CustomLink href=\"https://react.dev/reference/rsc/use-client\">\n          <code>'use client'</code>\n        </CustomLink>{\" \"}\n        directive at the top of the file to enable client side rendering, and\n        the{\" \"}\n        <CustomLink href=\"https://nextjs.authjs.dev/react#sessionprovider\">\n          <code>SessionProvider</code>\n        </CustomLink>{\" \"}\n        component in{\" \"}\n        <strong>\n          <code>client-example/page.tsx</code>\n        </strong>{\" \"}\n        to provide the session data.\n      </p>\n\n      <div className=\"flex flex-col gap-4 rounded-md bg-gray-100 p-4\">\n        <h2 className=\"text-xl font-bold\">Third-party backend integration</h2>\n        <p>\n          Press the button to send a request to our{\" \"}\n          <CustomLink href=\"https://github.com/nextauthjs/authjs-third-party-backend\">\n            <code>example backend</code>\n          </CustomLink>\n          . Read more{\" \"}\n          <CustomLink href=\"https://authjs.dev/guides/integrating-third-party-backends\">\n            <code>here</code>\n          </CustomLink>\n        </p>\n        <div className=\"flex flex-col\">\n          <Button\n            disabled={!session?.accessToken}\n            onClick={makeRequestWithToken}\n          >\n            Make API Request\n          </Button>\n        </div>\n        <pre>{apiResponse}</pre>\n        <p className=\"italic\">\n          Note: This example only works when using the Keycloak provider.\n        </p>\n      </div>\n\n      {status === \"loading\" ? (\n        <div>Loading...</div>\n      ) : (\n        <SessionData session={session} />\n      )}\n      <UpdateForm />\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs/components/custom-link.tsx",
    "content": "import { cn } from \"@/lib/utils\"\nimport { ExternalLink } from \"lucide-react\"\nimport Link from \"next/link\"\n\ninterface CustomLinkProps extends React.LinkHTMLAttributes<HTMLAnchorElement> {\n  href: string\n}\n\nconst CustomLink = ({\n  href,\n  children,\n  className,\n  ...rest\n}: CustomLinkProps) => {\n  const isInternalLink = href.startsWith(\"/\")\n  const isAnchorLink = href.startsWith(\"#\")\n\n  if (isInternalLink || isAnchorLink) {\n    return (\n      <Link href={href} className={className} {...rest}>\n        {children}\n      </Link>\n    )\n  }\n\n  return (\n    <Link\n      href={href}\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n      className={cn(\n        \"inline-flex items-center gap-1 align-baseline underline underline-offset-4\",\n        className\n      )}\n      {...rest}\n    >\n      <span>{children}</span>\n      <ExternalLink className=\"ml-0.5 inline-block h-4 w-4\" />\n    </Link>\n  )\n}\n\nexport default CustomLink\n"
  },
  {
    "path": "apps/examples/nextjs/components/footer.tsx",
    "content": "import CustomLink from \"./custom-link\"\nimport packageJSON from \"next-auth/package.json\"\n\nexport default function Footer() {\n  return (\n    <footer className=\"mx-0 my-4 flex w-full flex-col gap-4 px-4 text-sm sm:mx-auto sm:my-12 sm:h-5 sm:max-w-3xl sm:flex-row sm:items-center sm:justify-between sm:px-6\">\n      <div className=\"flex flex-col gap-4 sm:flex-row\">\n        <CustomLink href=\"https://nextjs.authjs.dev\">Documentation</CustomLink>\n        <CustomLink href=\"https://www.npmjs.com/package/next-auth\">\n          NPM\n        </CustomLink>\n        <CustomLink href=\"https://github.com/nextauthjs/next-auth/tree/main/apps/examples/nextjs\">\n          Source on GitHub\n        </CustomLink>\n        <CustomLink href=\"/policy\">Policy</CustomLink>\n      </div>\n      <div className=\"flex items-center justify-start gap-2\">\n        <img\n          className=\"size-5\"\n          src=\"https://authjs.dev/img/logo-sm.png\"\n          alt=\"Auth.js Logo\"\n        />\n        <CustomLink href=\"https://npmjs.org/package/next-auth\">\n          {packageJSON.version}\n        </CustomLink>\n      </div>\n    </footer>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs/components/header.tsx",
    "content": "import { MainNav } from \"./main-nav\"\nimport UserButton from \"./user-button\"\n\nexport default function Header() {\n  return (\n    <header className=\"sticky flex justify-center border-b\">\n      <div className=\"mx-auto flex h-16 w-full max-w-3xl items-center justify-between px-4 sm:px-6\">\n        <MainNav />\n        <UserButton />\n      </div>\n    </header>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs/components/main-nav.tsx",
    "content": "\"use client\"\n\nimport Image from \"next/image\"\n\nimport { cn } from \"@/lib/utils\"\nimport CustomLink from \"./custom-link\"\nimport {\n  NavigationMenu,\n  NavigationMenuContent,\n  NavigationMenuItem,\n  NavigationMenuLink,\n  NavigationMenuList,\n  NavigationMenuTrigger,\n  navigationMenuTriggerStyle,\n} from \"./ui/navigation-menu\"\nimport React from \"react\"\nimport { Button } from \"./ui/button\"\n\nexport function MainNav() {\n  return (\n    <div className=\"flex items-center gap-4\">\n      <CustomLink href=\"/\">\n        <Button variant=\"ghost\" className=\"p-0\">\n          <Image\n            src=\"/logo.png\"\n            alt=\"Home\"\n            width=\"32\"\n            height=\"32\"\n            className=\"min-w-8\"\n          />\n        </Button>\n      </CustomLink>\n      <NavigationMenu>\n        <NavigationMenuList>\n          <NavigationMenuItem>\n            <NavigationMenuTrigger className=\"px-2\">\n              Server Side\n            </NavigationMenuTrigger>\n            <NavigationMenuContent>\n              <ul className=\"grid gap-3 p-6 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]\">\n                <ListItem href=\"/server-example\" title=\"RSC Example\">\n                  Protecting React Server Component.\n                </ListItem>\n                <ListItem href=\"/middleware-example\" title=\"Middleware Example\">\n                  Using Middleware to protect pages & APIs.\n                </ListItem>\n                <ListItem href=\"/api-example\" title=\"Route Handler Example\">\n                  Getting the session inside an API Route.\n                </ListItem>\n              </ul>\n            </NavigationMenuContent>\n          </NavigationMenuItem>\n          <NavigationMenuItem>\n            <NavigationMenuLink\n              href=\"/client-example\"\n              className={navigationMenuTriggerStyle()}\n            >\n              Client Side\n            </NavigationMenuLink>\n          </NavigationMenuItem>\n        </NavigationMenuList>\n      </NavigationMenu>\n    </div>\n  )\n}\n\nconst ListItem = React.forwardRef<\n  React.ElementRef<\"a\">,\n  React.ComponentPropsWithoutRef<\"a\">\n>(({ className, title, children, ...props }, ref) => {\n  return (\n    <li>\n      <NavigationMenuLink asChild>\n        <a\n          ref={ref}\n          className={cn(\n            \"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors\",\n            className\n          )}\n          {...props}\n        >\n          <div className=\"text-sm font-medium leading-none\">{title}</div>\n          <p className=\"text-muted-foreground line-clamp-2 text-sm leading-snug\">\n            {children}\n          </p>\n        </a>\n      </NavigationMenuLink>\n    </li>\n  )\n})\nListItem.displayName = \"ListItem\"\n"
  },
  {
    "path": "apps/examples/nextjs/components/session-data.tsx",
    "content": "import type { Session } from \"next-auth\"\n\nexport default function SessionData({ session }: { session: Session | null }) {\n  if (session?.user) {\n    return (\n      <div className=\"flex w-full flex-col gap-4 rounded-md bg-gray-100 p-4\">\n        <h2 className=\"text-xl font-bold\">Current Session Data</h2>\n        {Object.keys(session.user).length > 3 ? (\n          <p>\n            In this example, the whole session object is passed to the page,\n            including the raw user object. Our recommendation is to{\" \"}\n            <em>only pass the necessary fields</em> to the page, as the raw user\n            object may contain sensitive information.\n          </p>\n        ) : (\n          <p>\n            In this example, only some fields in the user object is passed to\n            the page to avoid exposing sensitive information.\n          </p>\n        )}\n        <div className=\"flex flex-col rounded-md bg-neutral-100\">\n          <div className=\"rounded-t-md bg-neutral-200 p-4 font-bold\">\n            Session\n          </div>\n          <pre className=\"whitespace-pre-wrap break-all px-4 py-6\">\n            {JSON.stringify(session, null, 2)}\n          </pre>\n        </div>\n      </div>\n    )\n  }\n\n  return (\n    <p className=\"w-full rounded-md bg-gray-100 p-4\">\n      No session data, please <em>Sign In</em> first.\n    </p>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs/components/ui/avatar.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AvatarPrimitive from \"@radix-ui/react-avatar\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Avatar = React.forwardRef<\n  React.ElementRef<typeof AvatarPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>\n>(({ className, ...props }, ref) => (\n  <AvatarPrimitive.Root\n    ref={ref}\n    className={cn(\n      \"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full\",\n      className\n    )}\n    {...props}\n  />\n))\nAvatar.displayName = AvatarPrimitive.Root.displayName\n\nconst AvatarImage = React.forwardRef<\n  React.ElementRef<typeof AvatarPrimitive.Image>,\n  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>\n>(({ className, ...props }, ref) => (\n  <AvatarPrimitive.Image\n    ref={ref}\n    className={cn(\"aspect-square h-full w-full\", className)}\n    {...props}\n  />\n))\nAvatarImage.displayName = AvatarPrimitive.Image.displayName\n\nconst AvatarFallback = React.forwardRef<\n  React.ElementRef<typeof AvatarPrimitive.Fallback>,\n  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>\n>(({ className, ...props }, ref) => (\n  <AvatarPrimitive.Fallback\n    ref={ref}\n    className={cn(\n      \"bg-muted flex h-full w-full items-center justify-center rounded-full\",\n      className\n    )}\n    {...props}\n  />\n))\nAvatarFallback.displayName = AvatarPrimitive.Fallback.displayName\n\nexport { Avatar, AvatarImage, AvatarFallback }\n"
  },
  {
    "path": "apps/examples/nextjs/components/ui/button.tsx",
    "content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n        destructive:\n          \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n        outline:\n          \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n      size: {\n        default: \"h-10 px-4 py-2\",\n        sm: \"h-9 rounded-md px-3\",\n        lg: \"h-11 rounded-md px-8\",\n        icon: \"h-10 w-10\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nexport interface ButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n    VariantProps<typeof buttonVariants> {\n  asChild?: boolean\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n  ({ className, variant, size, asChild = false, ...props }, ref) => {\n    const Comp = asChild ? Slot : \"button\"\n    return (\n      <Comp\n        className={cn(buttonVariants({ variant, size, className }))}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n"
  },
  {
    "path": "apps/examples/nextjs/components/ui/dropdown-menu.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimport { Check, ChevronRight, Circle } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst DropdownMenu = DropdownMenuPrimitive.Root\n\nconst DropdownMenuTrigger = DropdownMenuPrimitive.Trigger\n\nconst DropdownMenuGroup = DropdownMenuPrimitive.Group\n\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal\n\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub\n\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {\n    inset?: boolean\n  }\n>(({ className, inset, children, ...props }, ref) => (\n  <DropdownMenuPrimitive.SubTrigger\n    ref={ref}\n    className={cn(\n      \"focus:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  >\n    {children}\n    <ChevronRight className=\"ml-auto h-4 w-4\" />\n  </DropdownMenuPrimitive.SubTrigger>\n))\nDropdownMenuSubTrigger.displayName =\n  DropdownMenuPrimitive.SubTrigger.displayName\n\nconst DropdownMenuSubContent = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, ...props }, ref) => (\n  <DropdownMenuPrimitive.SubContent\n    ref={ref}\n    className={cn(\n      \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg\",\n      className\n    )}\n    {...props}\n  />\n))\nDropdownMenuSubContent.displayName =\n  DropdownMenuPrimitive.SubContent.displayName\n\nconst DropdownMenuContent = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n  <DropdownMenuPrimitive.Portal>\n    <DropdownMenuPrimitive.Content\n      ref={ref}\n      sideOffset={sideOffset}\n      className={cn(\n        \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md\",\n        className\n      )}\n      {...props}\n    />\n  </DropdownMenuPrimitive.Portal>\n))\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName\n\nconst DropdownMenuItem = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {\n    inset?: boolean\n  }\n>(({ className, inset, ...props }, ref) => (\n  <DropdownMenuPrimitive.Item\n    ref={ref}\n    className={cn(\n      \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  />\n))\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n  <DropdownMenuPrimitive.CheckboxItem\n    ref={ref}\n    className={cn(\n      \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    checked={checked}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <DropdownMenuPrimitive.ItemIndicator>\n        <Check className=\"h-4 w-4\" />\n      </DropdownMenuPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </DropdownMenuPrimitive.CheckboxItem>\n))\nDropdownMenuCheckboxItem.displayName =\n  DropdownMenuPrimitive.CheckboxItem.displayName\n\nconst DropdownMenuRadioItem = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n  <DropdownMenuPrimitive.RadioItem\n    ref={ref}\n    className={cn(\n      \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <DropdownMenuPrimitive.ItemIndicator>\n        <Circle className=\"h-2 w-2 fill-current\" />\n      </DropdownMenuPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </DropdownMenuPrimitive.RadioItem>\n))\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName\n\nconst DropdownMenuLabel = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {\n    inset?: boolean\n  }\n>(({ className, inset, ...props }, ref) => (\n  <DropdownMenuPrimitive.Label\n    ref={ref}\n    className={cn(\n      \"px-2 py-1.5 text-sm font-semibold\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  />\n))\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName\n\nconst DropdownMenuSeparator = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n  <DropdownMenuPrimitive.Separator\n    ref={ref}\n    className={cn(\"bg-muted -mx-1 my-1 h-px\", className)}\n    {...props}\n  />\n))\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName\n\nconst DropdownMenuShortcut = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => {\n  return (\n    <span\n      className={cn(\"ml-auto text-xs tracking-widest opacity-60\", className)}\n      {...props}\n    />\n  )\n}\nDropdownMenuShortcut.displayName = \"DropdownMenuShortcut\"\n\nexport {\n  DropdownMenu,\n  DropdownMenuTrigger,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuCheckboxItem,\n  DropdownMenuRadioItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuGroup,\n  DropdownMenuPortal,\n  DropdownMenuSub,\n  DropdownMenuSubContent,\n  DropdownMenuSubTrigger,\n  DropdownMenuRadioGroup,\n}\n"
  },
  {
    "path": "apps/examples/nextjs/components/ui/input.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface InputProps\n  extends React.InputHTMLAttributes<HTMLInputElement> {}\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n  ({ className, type, ...props }, ref) => {\n    return (\n      <input\n        type={type}\n        className={cn(\n          \"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n          className\n        )}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nInput.displayName = \"Input\"\n\nexport { Input }\n"
  },
  {
    "path": "apps/examples/nextjs/components/ui/navigation-menu.tsx",
    "content": "import * as React from \"react\"\nimport * as NavigationMenuPrimitive from \"@radix-ui/react-navigation-menu\"\nimport { cva } from \"class-variance-authority\"\nimport { ChevronDown } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst NavigationMenu = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>\n>(({ className, children, ...props }, ref) => (\n  <NavigationMenuPrimitive.Root\n    ref={ref}\n    className={cn(\n      \"relative z-10 flex max-w-max flex-1 items-center justify-center\",\n      className\n    )}\n    {...props}\n  >\n    {children}\n    <NavigationMenuViewport />\n  </NavigationMenuPrimitive.Root>\n))\nNavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName\n\nconst NavigationMenuList = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.List>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>\n>(({ className, ...props }, ref) => (\n  <NavigationMenuPrimitive.List\n    ref={ref}\n    className={cn(\n      \"group flex flex-1 list-none items-center justify-center space-x-1\",\n      className\n    )}\n    {...props}\n  />\n))\nNavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName\n\nconst NavigationMenuItem = NavigationMenuPrimitive.Item\n\nconst navigationMenuTriggerStyle = cva(\n  \"group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-2 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50\"\n)\n\nconst NavigationMenuTrigger = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n  <NavigationMenuPrimitive.Trigger\n    ref={ref}\n    className={cn(navigationMenuTriggerStyle(), \"group\", className)}\n    {...props}\n  >\n    {children}{\" \"}\n    <ChevronDown\n      className=\"relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180\"\n      aria-hidden=\"true\"\n    />\n  </NavigationMenuPrimitive.Trigger>\n))\nNavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName\n\nconst NavigationMenuContent = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>\n>(({ className, ...props }, ref) => (\n  <NavigationMenuPrimitive.Content\n    ref={ref}\n    className={cn(\n      \"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 left-0 top-0 w-full md:absolute md:w-auto\",\n      className\n    )}\n    {...props}\n  />\n))\nNavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName\n\nconst NavigationMenuLink = NavigationMenuPrimitive.Link\n\nconst NavigationMenuViewport = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>\n>(({ className, ...props }, ref) => (\n  <div className={cn(\"absolute left-0 top-full flex justify-center\")}>\n    <NavigationMenuPrimitive.Viewport\n      className={cn(\n        \"origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow-lg md:w-[var(--radix-navigation-menu-viewport-width)]\",\n        className\n      )}\n      ref={ref}\n      {...props}\n    />\n  </div>\n))\nNavigationMenuViewport.displayName =\n  NavigationMenuPrimitive.Viewport.displayName\n\nconst NavigationMenuIndicator = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>\n>(({ className, ...props }, ref) => (\n  <NavigationMenuPrimitive.Indicator\n    ref={ref}\n    className={cn(\n      \"data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden\",\n      className\n    )}\n    {...props}\n  >\n    <div className=\"bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md\" />\n  </NavigationMenuPrimitive.Indicator>\n))\nNavigationMenuIndicator.displayName =\n  NavigationMenuPrimitive.Indicator.displayName\n\nexport {\n  navigationMenuTriggerStyle,\n  NavigationMenu,\n  NavigationMenuList,\n  NavigationMenuItem,\n  NavigationMenuContent,\n  NavigationMenuTrigger,\n  NavigationMenuLink,\n  NavigationMenuIndicator,\n  NavigationMenuViewport,\n}\n"
  },
  {
    "path": "apps/examples/nextjs/components/user-button.tsx",
    "content": "import { Avatar, AvatarFallback, AvatarImage } from \"./ui/avatar\"\nimport { Button } from \"./ui/button\"\nimport { auth } from \"auth\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuTrigger,\n} from \"./ui/dropdown-menu\"\nimport { SignIn, SignOut } from \"./auth-components\"\n\nexport default async function UserButton() {\n  const session = await auth()\n  if (!session?.user) return <SignIn />\n  return (\n    <div className=\"flex items-center gap-2\">\n      <span className=\"hidden text-sm sm:inline-flex\">\n        {session.user.email}\n      </span>\n      <DropdownMenu>\n        <DropdownMenuTrigger asChild>\n          <Button variant=\"ghost\" className=\"relative h-8 w-8 rounded-full\">\n            <Avatar className=\"h-8 w-8\">\n              <AvatarImage\n                src={\n                  session.user.image ??\n                  `https://api.dicebear.com/9.x/thumbs/svg?seed=${Math.floor(Math.random() * 100000) + 1}&randomizeIds=true`\n                }\n                alt={session.user.name ?? \"\"}\n              />\n            </Avatar>\n          </Button>\n        </DropdownMenuTrigger>\n        <DropdownMenuContent className=\"w-56\" align=\"end\" forceMount>\n          <DropdownMenuLabel className=\"font-normal\">\n            <div className=\"flex flex-col space-y-1\">\n              <p className=\"text-sm font-medium leading-none\">\n                {session.user.name}\n              </p>\n              <p className=\"text-muted-foreground text-xs leading-none\">\n                {session.user.email}\n              </p>\n            </div>\n          </DropdownMenuLabel>\n          <DropdownMenuItem>\n            <SignOut />\n          </DropdownMenuItem>\n        </DropdownMenuContent>\n      </DropdownMenu>\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs/components.json",
    "content": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"style\": \"default\",\n  \"rsc\": true,\n  \"tsx\": true,\n  \"tailwind\": {\n    \"config\": \"tailwind.config.js\",\n    \"css\": \"app/globals.css\",\n    \"baseColor\": \"slate\",\n    \"cssVariables\": true\n  },\n  \"aliases\": {\n    \"components\": \"@/components\",\n    \"utils\": \"@/lib/utils\"\n  }\n}\n"
  },
  {
    "path": "apps/examples/nextjs/docker-compose.yml",
    "content": "services:\n  authjs-docker-test:\n    build: .\n    environment:\n      - TEST_KEYCLOAK_USERNAME\n      - TEST_KEYCLOAK_PASSWORD\n      - AUTH_KEYCLOAK_ID\n      - AUTH_KEYCLOAK_SECRET\n      - AUTH_KEYCLOAK_ISSUER\n      - AUTH_SECRET=\"MohY0/2zSQw/psWEnejC2ka3Al0oifvY4YjOkUaFfnI=\"\n      - AUTH_URL=http://localhost:3000/auth\n    ports:\n      - \"3000:3000\"\n"
  },
  {
    "path": "apps/examples/nextjs/lib/utils.ts",
    "content": "import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n"
  },
  {
    "path": "apps/examples/nextjs/middleware.ts",
    "content": "export { auth as middleware } from \"auth\"\n\n// Or like this if you need to do something here.\n// export default auth((req) => {\n//   console.log(req.auth) //  { session: { user: { ... } } }\n// })\n\n// Read more: https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher\nexport const config = {\n  matcher: [\"/((?!api|_next/static|_next/image|favicon.ico).*)\"],\n}\n"
  },
  {
    "path": "apps/examples/nextjs/next.config.js",
    "content": "/** @type {import(\"next\").NextConfig} */\nmodule.exports = {\n  output: \"standalone\",\n}\n"
  },
  {
    "path": "apps/examples/nextjs/package.json",
    "content": "{\n  \"private\": true,\n  \"description\": \"An example project for NextAuth.js with Next.js\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth-example.git\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"homepage\": \"https://next-auth-example.vercel.app\",\n  \"scripts\": {\n    \"dev\": \"next\",\n    \"build\": \"next build\",\n    \"start\": \"next start\"\n  },\n  \"author\": \"Iain Collins <me@iaincollins.com>\",\n  \"contributors\": [\n    \"Balázs Orbán <info@balazsorban.com>\",\n    \"Nico Domino <yo@ndo.dev>\",\n    \"Lluis Agusti <hi@llu.lu>\",\n    \"Thang Huu Vu <hi@thvu.dev>\"\n  ],\n  \"dependencies\": {\n    \"@auth/unstorage-adapter\": \"^2.0.0\",\n    \"@radix-ui/react-avatar\": \"^1.0.3\",\n    \"@radix-ui/react-collapsible\": \"^1.0.3\",\n    \"@radix-ui/react-dropdown-menu\": \"^2.0.5\",\n    \"@radix-ui/react-navigation-menu\": \"^1.1.3\",\n    \"@radix-ui/react-slot\": \"^1.0.2\",\n    \"@simplewebauthn/server\": \"^9.0.3\",\n    \"@vercel/kv\": \"^1.0.1\",\n    \"class-variance-authority\": \"^0.7.0\",\n    \"clsx\": \"^2.0.0\",\n    \"lucide-react\": \"^0.274.0\",\n    \"next\": \"latest\",\n    \"next-auth\": \"beta\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"tailwind-merge\": \"^1.14.0\",\n    \"tailwindcss-animate\": \"^1.0.7\",\n    \"unstorage\": \"^1.10.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.12.8\",\n    \"@types/react\": \"^18.2.23\",\n    \"@types/react-dom\": \"^18.2.8\",\n    \"autoprefixer\": \"^10.4.15\",\n    \"postcss\": \"^8.4.29\",\n    \"tailwindcss\": \"^3.3.3\",\n    \"typescript\": \"^5.2.2\"\n  },\n  \"engines\": {\n    \"node\": \">=20.0.0\"\n  }\n}\n"
  },
  {
    "path": "apps/examples/nextjs/postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "apps/examples/nextjs/tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  darkMode: \"selector\",\n  content: [\n    \"./pages/**/*.{ts,tsx}\",\n    \"./components/**/*.{ts,tsx}\",\n    \"./app/**/*.{ts,tsx}\",\n    \"./src/**/*.{ts,tsx}\",\n  ],\n  theme: {\n    container: {\n      center: true,\n      padding: \"2rem\",\n      screens: {\n        \"2xl\": \"1400px\",\n      },\n    },\n    extend: {\n      colors: {\n        border: \"hsl(var(--border))\",\n        input: \"hsl(var(--input))\",\n        ring: \"hsl(var(--ring))\",\n        background: \"hsl(var(--background))\",\n        foreground: \"hsl(var(--foreground))\",\n        primary: {\n          DEFAULT: \"hsl(var(--primary))\",\n          foreground: \"hsl(var(--primary-foreground))\",\n        },\n        secondary: {\n          DEFAULT: \"hsl(var(--secondary))\",\n          foreground: \"hsl(var(--secondary-foreground))\",\n        },\n        destructive: {\n          DEFAULT: \"hsl(var(--destructive))\",\n          foreground: \"hsl(var(--destructive-foreground))\",\n        },\n        muted: {\n          DEFAULT: \"hsl(var(--muted))\",\n          foreground: \"hsl(var(--muted-foreground))\",\n        },\n        accent: {\n          DEFAULT: \"hsl(var(--accent))\",\n          foreground: \"hsl(var(--accent-foreground))\",\n        },\n        popover: {\n          DEFAULT: \"hsl(var(--popover))\",\n          foreground: \"hsl(var(--popover-foreground))\",\n        },\n        card: {\n          DEFAULT: \"hsl(var(--card))\",\n          foreground: \"hsl(var(--card-foreground))\",\n        },\n      },\n      borderRadius: {\n        lg: \"var(--radius)\",\n        md: \"calc(var(--radius) - 2px)\",\n        sm: \"calc(var(--radius) - 4px)\",\n      },\n      keyframes: {\n        \"accordion-down\": {\n          from: { height: 0 },\n          to: { height: \"var(--radix-accordion-content-height)\" },\n        },\n        \"accordion-up\": {\n          from: { height: \"var(--radix-accordion-content-height)\" },\n          to: { height: 0 },\n        },\n      },\n      animation: {\n        \"accordion-down\": \"accordion-down 0.2s ease-out\",\n        \"accordion-up\": \"accordion-up 0.2s ease-out\",\n      },\n    },\n  },\n  plugins: [require(\"tailwindcss-animate\")],\n}\n"
  },
  {
    "path": "apps/examples/nextjs/test-docker.sh",
    "content": "#!/usr/bin/env bash\n\n# Easier to read `docker compose up` output\n# export BUILDKIT_PROGRESS=plain\n\nargs=(\"-f\" \"docker-compose.yml\")\nif [[ -z \"${CI}\" ]]; then\n  args+=(\"--env-file\" \".env\")\nfi\nargs+=(\"up\" \"--detach\" \"--build\")\n\necho \"Running: docker compose ${args[*]}\"\n\nif ! docker compose \"${args[@]}\"; then\n  echo \"Failed to start container\"\n  exit 1\nfi\n\necho \"waiting 10 seconds for container to start...\"\nsleep 10\n\n# Used to control which env vars to load in the playwright process\nexport TEST_DOCKER=1\n\n# Always stop container, but exit with 1 when tests are failing\nif playwright test -c ../../../packages/utils/playwright.config.ts; then\n  docker compose down\nelse\n  docker compose down && exit 1\nfi\n"
  },
  {
    "path": "apps/examples/nextjs/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\"./*\"],\n      \"auth\": [\"./auth\"]\n    }\n  },\n  \"include\": [\n    \"process.d.ts\",\n    \"next-env.d.ts\",\n    \"**/*.ts\",\n    \"**/*.tsx\",\n    \".next/types/**/*.ts\"\n  ],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/.gitignore",
    "content": ".DS_Store\n\nnode_modules/\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.yarn-integrity\n.npm\n\n.eslintcache\n\n*.tsbuildinfo\nnext-env.d.ts\n\n.next\n.vercel\n.env*.local"
  },
  {
    "path": "apps/examples/nextjs-pages/README.md",
    "content": "> The example repository is maintained from a [monorepo](https://github.com/nextauthjs/next-auth/tree/main/apps/examples/nextjs). Pull Requests should be opened against [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth).\n\n<p align=\"center\">\n   <br/>\n   <a href=\"https://authjs.dev\" target=\"_blank\"><img width=\"150px\" src=\"https://authjs.dev/img/logo-sm.png\" /></a>\n   <h3 align=\"center\">NextAuth.js Example App</h3>\n   <p align=\"center\">\n   Open Source. Full Stack. Own Your Data.\n   </p>\n   <p align=\"center\" style=\"align: center;\">\n      <a href=\"https://npm.im/next-auth\">\n        <img alt=\"npm\" src=\"https://img.shields.io/npm/v/next-auth?color=green&label=next-auth\">\n      </a>\n      <a href=\"https://bundlephobia.com/result?p=next-auth-example\">\n        <img src=\"https://img.shields.io/bundlephobia/minzip/next-auth?label=next-auth\" alt=\"Bundle Size\"/>\n      </a>\n      <a href=\"https://www.npmtrends.com/next-auth\">\n        <img src=\"https://img.shields.io/npm/dm/next-auth?label=next-auth%20downloads\" alt=\"Downloads\" />\n      </a>\n      <a href=\"https://npm.im/next-auth\">\n        <img src=\"https://img.shields.io/badge/npm-TypeScript-blue\" alt=\"TypeScript\" />\n      </a>\n   </p>\n</p>\n\n## Overview\n\nNextAuth.js is a complete open source authentication solution.\n\nThis is an example application that shows how `next-auth` is applied to a basic Next.js app.\n\nThe deployed version can be found at [`next-auth-example.vercel.app`](https://next-auth-example.vercel.app)\n\n### About NextAuth.js\n\nNextAuth.js is an easy to implement, full-stack (client/server) open source authentication library originally designed for [Next.js](https://nextjs.org) and [Serverless](https://vercel.com). Our goal is to [support even more frameworks](https://github.com/nextauthjs/next-auth/issues/2294) in the future.\n\nGo to [next-auth.js.org](https://authjs.dev) for more information and documentation.\n\n> _NextAuth.js is not officially associated with Vercel or Next.js._\n\n## Getting Started\n\n### 1. Clone the repository and install dependencies\n\n```\ngit clone https://github.com/nextauthjs/next-auth-example.git\ncd next-auth-example\nnpm install\n```\n\n### 2. Configure your local environment\n\nCopy the .env.local.example file in this directory to .env.local (which will be ignored by Git):\n\n```\ncp .env.local.example .env.local\n```\n\nAdd details for one or more providers (e.g. Google, Twitter, GitHub, Email, etc).\n\n#### Database\n\nA database is needed to persist user accounts and to support email sign in. However, you can still use NextAuth.js for authentication without a database by using OAuth for authentication. If you do not specify a database, [JSON Web Tokens](https://jwt.io/introduction) will be enabled by default.\n\nYou **can** skip configuring a database and come back to it later if you want.\n\nFor more information about setting up a database, please check out the following links:\n\n- Docs: [authjs.dev/reference/core/adapters](https://authjs.dev/reference/core/adapters)\n\n### 3. Configure Authentication Providers\n\n1. Review and update options in `auth.ts` as needed.\n\n2. When setting up OAuth, in the developer admin page for each of your OAuth services, you should configure the callback URL to use a callback path of `{server}/api/auth/callback/{provider}`.\n\ne.g. For Google OAuth you would use: `http://localhost:3000/api/auth/callback/google`\n\nA list of configured providers and their callback URLs is available from the endpoint `api/auth/providers`. You can find more information at https://authjs.dev/getting-started/providers/oauth-tutorial\n\n1. You can also choose to specify an SMTP server for passwordless sign in via email.\n\n### 4. Start the application\n\nTo run your site locally, use:\n\n```\nnpm run dev\n```\n\nTo run it in production mode, use:\n\n```\nnpm run build\nnpm run start\n```\n\n### 5. Preparing for Production\n\nFollow the [Deployment documentation](https://authjs.dev/getting-started/deployment)\n\n## License\n\nISC\n"
  },
  {
    "path": "apps/examples/nextjs-pages/app/api/auth/[...nextauth]/route.ts",
    "content": "// import { handlers } from \"../../../auth\"\n// const { GET, POST } = handlers\n//\n// const handler = async (req, res) => {\n//   const { method, headers, query, body } = req\n//   const webRequest = {\n//     ...req,\n//     headers: new Headers(req.headers),\n//     url: new URL(`http://${req.headers[\"x-forwarded-host\"]}${req.url}`),\n//   }\n//\n//   switch (method) {\n//     case \"GET\":\n//       res.send(GET(webRequest))\n//       break\n//     case \"POST\":\n//       res.send(POST(webRequest))\n//       break\n//   }\n// }\n//\n// export default handler\n\nimport { handlers } from \"auth\"\nexport const { GET, POST } = handlers\n"
  },
  {
    "path": "apps/examples/nextjs-pages/auth.ts",
    "content": "import NextAuth from \"next-auth\"\n\n// import Apple from \"next-auth/providers/apple\"\n// import Auth0 from \"next-auth/providers/auth0\"\n// import Authentik from \"next-auth/providers/authentik\"\n// import AzureAD from \"next-auth/providers/azure-ad\"\n// import AzureB2C from \"next-auth/providers/azure-ad-b2c\"\n// import Battlenet from \"next-auth/providers/battlenet\"\n// import Box from \"next-auth/providers/box\"\n// import BoxyHQSAML from \"next-auth/providers/boxyhq-saml\"\n// import Bungie from \"next-auth/providers/bungie\"\n// import Cognito from \"next-auth/providers/cognito\"\n// import Coinbase from \"next-auth/providers/coinbase\"\n// import Discord from \"next-auth/providers/discord\"\n// import Dropbox from \"next-auth/providers/dropbox\"\n// import DuendeIDS6 from \"next-auth/providers/duende-identity-server6\"\n// import Eveonline from \"next-auth/providers/eveonline\"\n// import Facebook from \"next-auth/providers/facebook\"\n// import Faceit from \"next-auth/providers/faceit\"\n// import FortyTwoSchool from \"next-auth/providers/42-school\"\n// import Foursquare from \"next-auth/providers/foursquare\"\n// import Freshbooks from \"next-auth/providers/freshbooks\"\n// import Fusionauth from \"next-auth/providers/fusionauth\"\nimport GitHub from \"next-auth/providers/github\"\n// import GitLab from \"next-auth/providers/gitlab\"\n// import Google from \"next-auth/providers/google\"\n// import Hubspot from \"next-auth/providers/hubspot\"\n// import Instagram from \"next-auth/providers/instagram\"\n// import Kakao from \"next-auth/providers/kakao\"\n// import Keycloak from \"next-auth/providers/keycloak\"\n// import Line from \"next-auth/providers/line\"\n// import LinkedIn from \"next-auth/providers/linkedin\"\n// import Mailchimp from \"next-auth/providers/mailchimp\"\n// import Mailru from \"next-auth/providers/mailru\"\n// import Medium from \"next-auth/providers/medium\"\n// import Naver from \"next-auth/providers/naver\"\n// import Netlify from \"next-auth/providers/netlify\"\n// import Okta from \"next-auth/providers/okta\"\n// import Onelogin from \"next-auth/providers/onelogin\"\n// import Osso from \"next-auth/providers/osso\"\n// import Osu from \"next-auth/providers/osu\"\n// import Passage from \"next-auth/providers/passage\"\n// import Patreon from \"next-auth/providers/patreon\"\n// import Pinterest from \"next-auth/providers/pinterest\"\n// import Pipedrive from \"next-auth/providers/pipedrive\"\n// import Reddit from \"next-auth/providers/reddit\"\n// import Salesforce from \"next-auth/providers/salesforce\"\n// import Slack from \"next-auth/providers/slack\"\n// import Spotify from \"next-auth/providers/spotify\"\n// import Strava from \"next-auth/providers/strava\"\n// import Todoist from \"next-auth/providers/todoist\"\n// import Trakt from \"next-auth/providers/trakt\"\n// import Twitch from \"next-auth/providers/twitch\"\n// import Twitter from \"next-auth/providers/twitter\"\n// import UnitedEffects from \"next-auth/providers/united-effects\"\n// import Vk from \"next-auth/providers/vk\"\n// import Wikimedia from \"next-auth/providers/wikimedia\"\n// import WordPress from \"next-auth/providers/wordpress\"\n// import WorkOS from \"next-auth/providers/workos\"\n// import Yandex from \"next-auth/providers/yandex\"\n// import Zitadel from \"next-auth/providers/zitadel\"\n// import Zoho from \"next-auth/providers/zoho\"\n// import Zoom from \"next-auth/providers/zoom\"\n\nimport type { NextAuthConfig } from \"next-auth\"\n\nexport const config = {\n  theme: {\n    logo: \"https://next-auth.js.org/img/logo-sm.png\",\n  },\n  providers: [\n    // Apple,\n    // Auth0,\n    // Authentik,\n    // AzureAD,\n    // AzureB2C,\n    // Battlenet,\n    // Box,\n    // BoxyHQSAML,\n    // Bungie,\n    // Cognito,\n    // Coinbase,\n    // Discord,\n    // Dropbox,\n    // DuendeIDS6,\n    // Eveonline,\n    // Facebook,\n    // Faceit,\n    // FortyTwoSchool,\n    // Foursquare,\n    // Freshbooks,\n    // Fusionauth,\n    GitHub,\n    // GitLab,\n    // Google,\n    // Hubspot,\n    // Instagram,\n    // Kakao,\n    // Keycloak,\n    // Line,\n    // LinkedIn,\n    // Mailchimp,\n    // Mailru,\n    // Medium,\n    // Naver,\n    // Netlify,\n    // Okta,\n    // Onelogin,\n    // Osso,\n    // Osu,\n    // Passage,\n    // Patreon,\n    // Pinterest,\n    // Pipedrive,\n    // Reddit,\n    // Salesforce,\n    // Slack,\n    // Spotify,\n    // Strava,\n    // Todoist,\n    // Trakt,\n    // Twitch,\n    // Twitter,\n    // UnitedEffects,\n    // Vk,\n    // Wikimedia,\n    // WordPress,\n    // WorkOS,\n    // Yandex,\n    // Zitadel,\n    // Zoho,\n    // Zoom,\n  ],\n  callbacks: {\n    authorized({ request, auth }) {\n      const { pathname } = request.nextUrl\n      if (pathname === \"/middleware-example\") return !!auth\n      return true\n    },\n    jwt({ token, trigger, session }) {\n      if (trigger === \"update\") token.name = session.user.name\n      return token\n    },\n  },\n} satisfies NextAuthConfig\n\nexport const { handlers, auth, signIn, signOut } = NextAuth(config)\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components/auth-components.tsx",
    "content": "import { signIn, signOut } from \"next-auth/react\"\nimport { Button } from \"./ui/button\"\n\nexport function SignIn({\n  provider,\n  ...props\n}: { provider?: string } & React.ComponentPropsWithRef<typeof Button>) {\n  return (\n    <Button onClick={() => signIn()} {...props}>\n      Sign In\n    </Button>\n  )\n}\n\nexport function SignOut(props: React.ComponentPropsWithRef<typeof Button>) {\n  return (\n    <Button\n      onClick={() => signOut()}\n      variant=\"ghost\"\n      className=\"w-full p-0\"\n      {...props}\n    >\n      Sign Out\n    </Button>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components/client-example.tsx",
    "content": "import { useSession } from \"next-auth/react\"\nimport { Button } from \"./ui/button\"\nimport { Input } from \"./ui/input\"\nimport { useState } from \"react\"\nimport SessionData from \"./session-data\"\nimport CustomLink from \"./custom-link\"\n\nconst UpdateForm = () => {\n  const { data: session, update } = useSession()\n  const [name, setName] = useState(session?.user?.name ?? \"\")\n\n  if (!session?.user) return null\n  return (\n    <>\n      <h2 className=\"text-xl font-bold\">Updating the session</h2>\n      <div className=\"flex w-full max-w-sm items-center space-x-2\">\n        <Input\n          type=\"text\"\n          placeholder={session.user.name ?? \"\"}\n          value={name}\n          onChange={(e) => {\n            setName(e.target.value)\n          }}\n        />\n        <Button\n          type=\"submit\"\n          onClick={async () => {\n            if (session) {\n              await update({\n                ...session,\n                user: { ...session.user, name },\n              })\n            }\n          }}\n        >\n          Update\n        </Button>\n      </div>\n    </>\n  )\n}\n\nexport default function ClientExample() {\n  const { data: session, status } = useSession()\n  return (\n    <div className=\"mx-auto mt-10 max-w-screen-md space-y-4\">\n      <h1 className=\"text-3xl font-bold\">Client Side Rendering Usage</h1>\n      <p className=\"leading-loose\">\n        This page fetches session data client side using the{\" \"}\n        <CustomLink href=\"https://nextjs.authjs.dev/react#usesession\">\n          <code>useSession</code>\n        </CustomLink>{\" \"}\n        React Hook.\n      </p>\n      <p className=\"leading-loose\">\n        Make sure to wrap this component tree in a{\" \"}\n        <CustomLink href=\"https://nextjs.authjs.dev/react#sessionprovider\">\n          <code>SessionProvider</code>\n        </CustomLink>{\" \"}\n        component in{\" \"}\n        <strong>\n          <code>client-example/page.tsx</code>\n        </strong>{\" \"}\n        to provide the session data.\n      </p>\n\n      {status === \"loading\" ? (\n        <div>Loading...</div>\n      ) : (\n        <SessionData session={session} />\n      )}\n      <UpdateForm />\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components/custom-link.tsx",
    "content": "import { cn } from \"@/lib/utils\"\nimport { ExternalLink } from \"lucide-react\"\nimport Link from \"next/link\"\n\ninterface CustomLinkProps extends React.LinkHTMLAttributes<HTMLAnchorElement> {\n  href: string\n}\n\nconst CustomLink = ({\n  href,\n  children,\n  className,\n  ...rest\n}: CustomLinkProps) => {\n  const isInternalLink = href.startsWith(\"/\")\n  const isAnchorLink = href.startsWith(\"#\")\n\n  if (isInternalLink || isAnchorLink) {\n    return (\n      <Link href={href} className={className} {...rest}>\n        {children}\n      </Link>\n    )\n  }\n\n  return (\n    <Link\n      href={href}\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n      className={cn(\"inline-flex items-center underline\", className)}\n      {...rest}\n    >\n      {children}\n      <ExternalLink className=\"ml-0.5 inline-block h-4 w-4\" />\n    </Link>\n  )\n}\n\nexport default CustomLink\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components/footer.tsx",
    "content": "import CustomLink from \"./custom-link\"\n\nexport default function Footer() {\n  return (\n    <footer className=\"mx-0 my-4 flex w-full flex-col space-y-1 px-4 text-sm sm:px-6 md:mx-auto md:my-12 md:h-5 md:max-w-3xl md:flex-row md:items-center md:space-x-4 md:space-y-0\">\n      <CustomLink href=\"https://nextjs.authjs.dev\">Documentation</CustomLink>\n      <CustomLink href=\"https://www.npmjs.com/package/next-auth\">\n        NPM\n      </CustomLink>\n      <CustomLink href=\"https://github.com/nextauthjs/next-auth/tree/main/apps/examples/nextjs-pages\">\n        Source on GitHub\n      </CustomLink>\n      <CustomLink href=\"/policy\">Policy</CustomLink>\n    </footer>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components/header.tsx",
    "content": "import { MainNav } from \"./main-nav\"\nimport UserButton from \"./user-button\"\n\nexport default function Header() {\n  return (\n    <header className=\"sticky flex justify-center border-b\">\n      <div className=\"mx-auto flex h-[4.5rem] w-full max-w-3xl items-center justify-between px-4 sm:px-6\">\n        <MainNav />\n        <UserButton />\n      </div>\n    </header>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components/main-nav.tsx",
    "content": "import Image from \"next/image\"\n\nimport { cn } from \"@/lib/utils\"\nimport CustomLink from \"./custom-link\"\nimport {\n  NavigationMenu,\n  NavigationMenuContent,\n  NavigationMenuItem,\n  NavigationMenuLink,\n  NavigationMenuList,\n  NavigationMenuTrigger,\n  navigationMenuTriggerStyle,\n} from \"./ui/navigation-menu\"\nimport React from \"react\"\n\nexport function MainNav() {\n  return (\n    <div className=\"flex h-full items-center space-x-2 lg:space-x-4\">\n      <CustomLink href=\"/\" className=\"p-4\">\n        <Image src=\"/logo.png\" alt=\"Home\" width=\"32\" height=\"32\" />\n      </CustomLink>\n      <NavigationMenu>\n        <NavigationMenuList>\n          <NavigationMenuItem>\n            <NavigationMenuTrigger>Server Side</NavigationMenuTrigger>\n            <NavigationMenuContent>\n              <ul className=\"grid gap-3 p-6 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]\">\n                <ListItem href=\"/server-example\" title=\"getServerSideProps\">\n                  Protecting React SSR pages.\n                </ListItem>\n                <ListItem href=\"/middleware-example\" title=\"Middleware Example\">\n                  Using Middleware to protect pages & APIs.\n                </ListItem>\n                <ListItem href=\"/api-example\" title=\"Route Handler Example\">\n                  Getting the session inside an API Route.\n                </ListItem>\n              </ul>\n            </NavigationMenuContent>\n          </NavigationMenuItem>\n          <NavigationMenuItem>\n            <NavigationMenuLink\n              href=\"/client-example\"\n              className={navigationMenuTriggerStyle()}\n            >\n              Client Side\n            </NavigationMenuLink>\n          </NavigationMenuItem>\n        </NavigationMenuList>\n      </NavigationMenu>\n    </div>\n  )\n}\n\nconst ListItem = React.forwardRef<\n  React.ElementRef<\"a\">,\n  React.ComponentPropsWithoutRef<\"a\">\n>(({ className, title, children, ...props }, ref) => {\n  return (\n    <li>\n      <NavigationMenuLink asChild>\n        <a\n          ref={ref}\n          className={cn(\n            \"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors\",\n            className\n          )}\n          {...props}\n        >\n          <div className=\"text-sm font-medium leading-none\">{title}</div>\n          <p className=\"text-muted-foreground line-clamp-2 text-sm leading-snug\">\n            {children}\n          </p>\n        </a>\n      </NavigationMenuLink>\n    </li>\n  )\n})\nListItem.displayName = \"ListItem\"\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components/session-data.tsx",
    "content": "import type { Session } from \"next-auth\"\n\nexport default function SessionData({ session }: { session: Session | null }) {\n  console.log(\"session-data.session\", session)\n  if (session?.user) {\n    return (\n      <div className=\"w-full space-y-2 overflow-auto rounded-md bg-gray-100 p-4\">\n        <h2 className=\"text-xl font-bold\">Current Session Data</h2>\n        {Object.keys(session.user).length > 3 ? (\n          <blockquote className=\"border-l-4 border-gray-300 pl-2\">\n            In this example, the whole session object is passed to the page,\n            including the raw user object. Our recommendation is to{\" \"}\n            <em>only pass the necessary fields</em> to the page, as the raw user\n            object may contain sensitive information.\n          </blockquote>\n        ) : (\n          <blockquote className=\"border-l-4 border-gray-300 pl-2\">\n            In this example, only some fields in the user object is passed to\n            the page to avoid exposing sensitive information.\n          </blockquote>\n        )}\n        <pre>{JSON.stringify(session, null, 2)}</pre>\n      </div>\n    )\n  }\n\n  return (\n    <p className=\"w-full space-y-2 overflow-auto rounded-md bg-gray-100 p-4\">\n      No session data, please <b>Sign In</b> first.\n    </p>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components/ui/avatar.tsx",
    "content": "import * as React from \"react\"\nimport * as AvatarPrimitive from \"@radix-ui/react-avatar\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Avatar = React.forwardRef<\n  React.ElementRef<typeof AvatarPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>\n>(({ className, ...props }, ref) => (\n  <AvatarPrimitive.Root\n    ref={ref}\n    className={cn(\n      \"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full\",\n      className\n    )}\n    {...props}\n  />\n))\nAvatar.displayName = AvatarPrimitive.Root.displayName\n\nconst AvatarImage = React.forwardRef<\n  React.ElementRef<typeof AvatarPrimitive.Image>,\n  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>\n>(({ className, ...props }, ref) => (\n  <AvatarPrimitive.Image\n    ref={ref}\n    className={cn(\"aspect-square h-full w-full\", className)}\n    {...props}\n  />\n))\nAvatarImage.displayName = AvatarPrimitive.Image.displayName\n\nconst AvatarFallback = React.forwardRef<\n  React.ElementRef<typeof AvatarPrimitive.Fallback>,\n  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>\n>(({ className, ...props }, ref) => (\n  <AvatarPrimitive.Fallback\n    ref={ref}\n    className={cn(\n      \"bg-muted flex h-full w-full items-center justify-center rounded-full\",\n      className\n    )}\n    {...props}\n  />\n))\nAvatarFallback.displayName = AvatarPrimitive.Fallback.displayName\n\nexport { Avatar, AvatarImage, AvatarFallback }\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components/ui/button.tsx",
    "content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n        destructive:\n          \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n        outline:\n          \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n      size: {\n        default: \"h-10 px-4 py-2\",\n        sm: \"h-9 rounded-md px-3\",\n        lg: \"h-11 rounded-md px-8\",\n        icon: \"h-10 w-10\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nexport interface ButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n    VariantProps<typeof buttonVariants> {\n  asChild?: boolean\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n  ({ className, variant, size, asChild = false, ...props }, ref) => {\n    const Comp = asChild ? Slot : \"button\"\n    return (\n      <Comp\n        className={cn(buttonVariants({ variant, size, className }))}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components/ui/dropdown-menu.tsx",
    "content": "import * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimport { Check, ChevronRight, Circle } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst DropdownMenu = DropdownMenuPrimitive.Root\n\nconst DropdownMenuTrigger = DropdownMenuPrimitive.Trigger\n\nconst DropdownMenuGroup = DropdownMenuPrimitive.Group\n\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal\n\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub\n\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {\n    inset?: boolean\n  }\n>(({ className, inset, children, ...props }, ref) => (\n  <DropdownMenuPrimitive.SubTrigger\n    ref={ref}\n    className={cn(\n      \"focus:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  >\n    {children}\n    <ChevronRight className=\"ml-auto h-4 w-4\" />\n  </DropdownMenuPrimitive.SubTrigger>\n))\nDropdownMenuSubTrigger.displayName =\n  DropdownMenuPrimitive.SubTrigger.displayName\n\nconst DropdownMenuSubContent = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, ...props }, ref) => (\n  <DropdownMenuPrimitive.SubContent\n    ref={ref}\n    className={cn(\n      \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg\",\n      className\n    )}\n    {...props}\n  />\n))\nDropdownMenuSubContent.displayName =\n  DropdownMenuPrimitive.SubContent.displayName\n\nconst DropdownMenuContent = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n  <DropdownMenuPrimitive.Portal>\n    <DropdownMenuPrimitive.Content\n      ref={ref}\n      sideOffset={sideOffset}\n      className={cn(\n        \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md\",\n        className\n      )}\n      {...props}\n    />\n  </DropdownMenuPrimitive.Portal>\n))\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName\n\nconst DropdownMenuItem = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {\n    inset?: boolean\n  }\n>(({ className, inset, ...props }, ref) => (\n  <DropdownMenuPrimitive.Item\n    ref={ref}\n    className={cn(\n      \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  />\n))\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n  <DropdownMenuPrimitive.CheckboxItem\n    ref={ref}\n    className={cn(\n      \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    checked={checked}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <DropdownMenuPrimitive.ItemIndicator>\n        <Check className=\"h-4 w-4\" />\n      </DropdownMenuPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </DropdownMenuPrimitive.CheckboxItem>\n))\nDropdownMenuCheckboxItem.displayName =\n  DropdownMenuPrimitive.CheckboxItem.displayName\n\nconst DropdownMenuRadioItem = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n  <DropdownMenuPrimitive.RadioItem\n    ref={ref}\n    className={cn(\n      \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    {...props}\n  >\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <DropdownMenuPrimitive.ItemIndicator>\n        <Circle className=\"h-2 w-2 fill-current\" />\n      </DropdownMenuPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </DropdownMenuPrimitive.RadioItem>\n))\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName\n\nconst DropdownMenuLabel = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {\n    inset?: boolean\n  }\n>(({ className, inset, ...props }, ref) => (\n  <DropdownMenuPrimitive.Label\n    ref={ref}\n    className={cn(\n      \"px-2 py-1.5 text-sm font-semibold\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}\n  />\n))\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName\n\nconst DropdownMenuSeparator = React.forwardRef<\n  React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n  <DropdownMenuPrimitive.Separator\n    ref={ref}\n    className={cn(\"bg-muted -mx-1 my-1 h-px\", className)}\n    {...props}\n  />\n))\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName\n\nconst DropdownMenuShortcut = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => {\n  return (\n    <span\n      className={cn(\"ml-auto text-xs tracking-widest opacity-60\", className)}\n      {...props}\n    />\n  )\n}\nDropdownMenuShortcut.displayName = \"DropdownMenuShortcut\"\n\nexport {\n  DropdownMenu,\n  DropdownMenuTrigger,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuCheckboxItem,\n  DropdownMenuRadioItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuGroup,\n  DropdownMenuPortal,\n  DropdownMenuSub,\n  DropdownMenuSubContent,\n  DropdownMenuSubTrigger,\n  DropdownMenuRadioGroup,\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components/ui/input.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface InputProps\n  extends React.InputHTMLAttributes<HTMLInputElement> {}\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n  ({ className, type, ...props }, ref) => {\n    return (\n      <input\n        type={type}\n        className={cn(\n          \"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n          className\n        )}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nInput.displayName = \"Input\"\n\nexport { Input }\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components/ui/navigation-menu.tsx",
    "content": "import * as React from \"react\"\nimport * as NavigationMenuPrimitive from \"@radix-ui/react-navigation-menu\"\nimport { cva } from \"class-variance-authority\"\nimport { ChevronDown } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst NavigationMenu = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>\n>(({ className, children, ...props }, ref) => (\n  <NavigationMenuPrimitive.Root\n    ref={ref}\n    className={cn(\n      \"relative z-10 flex max-w-max flex-1 items-center justify-center\",\n      className\n    )}\n    {...props}\n  >\n    {children}\n    <NavigationMenuViewport />\n  </NavigationMenuPrimitive.Root>\n))\nNavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName\n\nconst NavigationMenuList = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.List>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>\n>(({ className, ...props }, ref) => (\n  <NavigationMenuPrimitive.List\n    ref={ref}\n    className={cn(\n      \"group flex flex-1 list-none items-center justify-center space-x-1\",\n      className\n    )}\n    {...props}\n  />\n))\nNavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName\n\nconst NavigationMenuItem = NavigationMenuPrimitive.Item\n\nconst navigationMenuTriggerStyle = cva(\n  \"group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50\"\n)\n\nconst NavigationMenuTrigger = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n  <NavigationMenuPrimitive.Trigger\n    ref={ref}\n    className={cn(navigationMenuTriggerStyle(), \"group\", className)}\n    {...props}\n  >\n    {children}{\" \"}\n    <ChevronDown\n      className=\"relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180\"\n      aria-hidden=\"true\"\n    />\n  </NavigationMenuPrimitive.Trigger>\n))\nNavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName\n\nconst NavigationMenuContent = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>\n>(({ className, ...props }, ref) => (\n  <NavigationMenuPrimitive.Content\n    ref={ref}\n    className={cn(\n      \"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 left-0 top-0 w-full md:absolute md:w-auto\",\n      className\n    )}\n    {...props}\n  />\n))\nNavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName\n\nconst NavigationMenuLink = NavigationMenuPrimitive.Link\n\nconst NavigationMenuViewport = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>\n>(({ className, ...props }, ref) => (\n  <div className={cn(\"absolute left-0 top-full flex justify-center\")}>\n    <NavigationMenuPrimitive.Viewport\n      className={cn(\n        \"origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow-lg md:w-[var(--radix-navigation-menu-viewport-width)]\",\n        className\n      )}\n      ref={ref}\n      {...props}\n    />\n  </div>\n))\nNavigationMenuViewport.displayName =\n  NavigationMenuPrimitive.Viewport.displayName\n\nconst NavigationMenuIndicator = React.forwardRef<\n  React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,\n  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>\n>(({ className, ...props }, ref) => (\n  <NavigationMenuPrimitive.Indicator\n    ref={ref}\n    className={cn(\n      \"data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden\",\n      className\n    )}\n    {...props}\n  >\n    <div className=\"bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md\" />\n  </NavigationMenuPrimitive.Indicator>\n))\nNavigationMenuIndicator.displayName =\n  NavigationMenuPrimitive.Indicator.displayName\n\nexport {\n  navigationMenuTriggerStyle,\n  NavigationMenu,\n  NavigationMenuList,\n  NavigationMenuItem,\n  NavigationMenuContent,\n  NavigationMenuTrigger,\n  NavigationMenuLink,\n  NavigationMenuIndicator,\n  NavigationMenuViewport,\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components/user-button.tsx",
    "content": "import { Avatar, AvatarFallback, AvatarImage } from \"./ui/avatar\"\nimport { Button } from \"./ui/button\"\nimport { useSession } from \"next-auth/react\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuTrigger,\n} from \"./ui/dropdown-menu\"\nimport { SignIn, SignOut } from \"./auth-components\"\n\nexport default function UserButton() {\n  const { data: session } = useSession()\n  if (!session?.user) return <SignIn />\n  return (\n    <DropdownMenu>\n      <DropdownMenuTrigger asChild>\n        <Button variant=\"ghost\" className=\"relative h-8 w-8 rounded-full\">\n          <Avatar className=\"h-8 w-8\">\n            {session.user.image && (\n              <AvatarImage\n                src={session.user.image}\n                alt={session.user.name ?? \"\"}\n              />\n            )}\n            <AvatarFallback>{session.user.email}</AvatarFallback>\n          </Avatar>\n        </Button>\n      </DropdownMenuTrigger>\n      <DropdownMenuContent className=\"w-32\" align=\"end\" forceMount>\n        <DropdownMenuLabel className=\"font-normal\">\n          <div className=\"flex flex-col space-y-1\">\n            <p className=\"text-sm font-medium leading-none\">\n              {session.user.name}\n            </p>\n            <p className=\"text-muted-foreground text-xs leading-none\">\n              {session.user.email}\n            </p>\n          </div>\n        </DropdownMenuLabel>\n        <DropdownMenuItem>\n          <SignOut />\n        </DropdownMenuItem>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/components.json",
    "content": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"style\": \"default\",\n  \"rsc\": true,\n  \"tsx\": true,\n  \"tailwind\": {\n    \"config\": \"tailwind.config.js\",\n    \"css\": \"app/globals.css\",\n    \"baseColor\": \"slate\",\n    \"cssVariables\": true\n  },\n  \"aliases\": {\n    \"components\": \"@/components\",\n    \"utils\": \"@/lib/utils\"\n  }\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/lib/utils.ts",
    "content": "import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/middleware.ts",
    "content": "export { auth as middleware } from \"auth\"\n\n// Read more: https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher\nexport const config = {\n  matcher: [\"/((?!api|_next/static|_next/image|favicon.ico).*)\"],\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/next.config.js",
    "content": "/** @type {import(\"next\").NextConfig} */\nmodule.exports = {}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/package.json",
    "content": "{\n  \"name\": \"next-auth-pages-example\",\n  \"private\": true,\n  \"description\": \"An example project for NextAuth.js with Next.js Pages\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth-pages-example.git\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"homepage\": \"https://next-auth-pages-example.vercel.app\",\n  \"scripts\": {\n    \"dev\": \"next\",\n    \"build\": \"next build\",\n    \"start\": \"next start\"\n  },\n  \"author\": \"Iain Collins <me@iaincollins.com>\",\n  \"contributors\": [\n    \"Balázs Orbán <info@balazsorban.com>\",\n    \"Nico Domino <yo@ndo.dev>\",\n    \"Lluis Agusti <hi@llu.lu>\",\n    \"Thang Huu Vu <hi@thvu.dev>\"\n  ],\n  \"dependencies\": {\n    \"@radix-ui/react-avatar\": \"^1.0.3\",\n    \"@radix-ui/react-collapsible\": \"^1.0.3\",\n    \"@radix-ui/react-dropdown-menu\": \"^2.0.5\",\n    \"@radix-ui/react-navigation-menu\": \"^1.1.3\",\n    \"@radix-ui/react-slot\": \"^1.0.2\",\n    \"class-variance-authority\": \"^0.7.0\",\n    \"clsx\": \"^2.0.0\",\n    \"lucide-react\": \"^0.274.0\",\n    \"next\": \"latest\",\n    \"next-auth\": \"beta\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"tailwind-merge\": \"^1.14.0\",\n    \"tailwindcss-animate\": \"^1.0.7\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^18\",\n    \"@types/react\": \"^18.2.23\",\n    \"@types/react-dom\": \"^18.2.8\",\n    \"autoprefixer\": \"^10.4.15\",\n    \"postcss\": \"^8.4.29\",\n    \"tailwindcss\": \"^3.3.3\",\n    \"typescript\": \"^5.2.2\"\n  }\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/pages/_app.tsx",
    "content": "import \"./globals.css\"\nimport { cn } from \"@/lib/utils\"\nimport Footer from \"@/components/footer\"\nimport Header from \"@/components/header\"\nimport { Inter } from \"next/font/google\"\nimport { SessionProvider } from \"next-auth/react\"\nimport type { AppProps } from \"next/app\"\n\nconst inter = Inter({ subsets: [\"latin\"] })\n\nexport default function MyApp({\n  Component,\n  pageProps: { session, ...pageProps },\n}: AppProps) {\n  return (\n    <SessionProvider session={session}>\n      <div className={cn(\"flex h-dvh flex-col\", inter.className)}>\n        <Header />\n        <div className=\"flex-grow\">\n          <Component {...pageProps} />\n        </div>\n        <Footer />\n      </div>\n    </SessionProvider>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/pages/api/protected.ts",
    "content": "// import { auth } from \"../../auth\"\n// import { getSession } from \"next-auth/react\"\nimport { NextApiRequest, NextApiResponse } from \"next\"\n\nexport default async function handler(\n  req: NextApiRequest,\n  res: NextApiResponse\n) {\n  // const session = await auth(req, res)\n  // const session = await getSession(req, res)\n  const url = `${req.headers[\"x-forwarded-proto\"]}://${req.headers.host}/api/auth/session`\n\n  // TODO: Test while working on other methods\n  const sessionRes = await fetch(url)\n  const session = await sessionRes.json()\n\n  if (session) {\n    return res.json({ data: \"Protected data\" })\n  }\n\n  return res.status(401).json({ message: \"Not authenticated\" })\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/pages/api-example/index.tsx",
    "content": "import CustomLink from \"@/components/custom-link\"\nimport { useEffect, useState } from \"react\"\n\nexport default function Page() {\n  const [data, setData] = useState()\n  useEffect(() => {\n    ;(async () => {\n      const res = await fetch(\"/api/protected\")\n      const json = await res.json()\n      console.log(\"protected.json\", data)\n      setData(json)\n    })()\n  }, [])\n  return (\n    <div className=\"mx-auto mt-10 max-w-screen-md space-y-4\">\n      <h1 className=\"text-3xl font-bold\">Route Handler Usage</h1>\n      <p className=\"leading-loose\">\n        This page fetches data from an API{\" \"}\n        <CustomLink href=\"https://nextjs.org/docs/pages/building-your-application/routing/api-routes\">\n          Route\n        </CustomLink>\n        . The API is protected using the universal{\" \"}\n        <CustomLink href=\"https://nextjs.authjs.dev#auth\">\n          <code>auth()</code>\n        </CustomLink>{\" \"}\n        method.\n      </p>\n      <h2 className=\"text-xl font-bold\">Data from API Route:</h2>\n      {data ? (\n        <pre className=\"w-full space-y-2 overflow-auto rounded-md bg-gray-100 p-4\">\n          <code>{JSON.stringify(data, null, 2)}</code>\n        </pre>\n      ) : (\n        <p className=\"w-full space-y-2 overflow-auto rounded-md bg-gray-100 p-4\">\n          No data from API Route, please <b>Sign In</b> first.\n        </p>\n      )}\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/pages/client-example/index.tsx",
    "content": "import { useSession } from \"next-auth/react\"\nimport ClientExample from \"@/components/client-example\"\nimport { SessionProvider } from \"next-auth/react\"\n\nexport default function ClientPage() {\n  const { data: session } = useSession()\n  if (session?.user) {\n    // TODO: Look into https://react.dev/reference/react/experimental_taintObjectReference\n    // filter out sensitive data before passing to client.\n    session.user = {\n      name: session.user.name,\n      email: session.user.email,\n      image: session.user.image,\n    }\n  }\n\n  return (\n    <SessionProvider session={session}>\n      <ClientExample />\n    </SessionProvider>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/pages/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --foreground: 222.2 84% 4.9%;\n\n    --card: 0 0% 100%;\n    --card-foreground: 222.2 84% 4.9%;\n\n    --popover: 0 0% 100%;\n    --popover-foreground: 222.2 84% 4.9%;\n\n    --primary: 222.2 47.4% 11.2%;\n    --primary-foreground: 210 40% 98%;\n\n    --secondary: 210 40% 96.1%;\n    --secondary-foreground: 222.2 47.4% 11.2%;\n\n    --muted: 210 40% 96.1%;\n    --muted-foreground: 215.4 16.3% 46.9%;\n\n    --accent: 210 40% 96.1%;\n    --accent-foreground: 222.2 47.4% 11.2%;\n\n    --destructive: 0 84.2% 60.2%;\n    --destructive-foreground: 210 40% 98%;\n\n    --border: 214.3 31.8% 91.4%;\n    --input: 214.3 31.8% 91.4%;\n    --ring: 222.2 84% 4.9%;\n\n    --radius: 0.5rem;\n  }\n\n  .dark {\n    --background: 222.2 84% 4.9%;\n    --foreground: 210 40% 98%;\n\n    --card: 222.2 84% 4.9%;\n    --card-foreground: 210 40% 98%;\n\n    --popover: 222.2 84% 4.9%;\n    --popover-foreground: 210 40% 98%;\n\n    --primary: 210 40% 98%;\n    --primary-foreground: 222.2 47.4% 11.2%;\n\n    --secondary: 217.2 32.6% 17.5%;\n    --secondary-foreground: 210 40% 98%;\n\n    --muted: 217.2 32.6% 17.5%;\n    --muted-foreground: 215 20.2% 65.1%;\n\n    --accent: 217.2 32.6% 17.5%;\n    --accent-foreground: 210 40% 98%;\n\n    --destructive: 0 62.8% 30.6%;\n    --destructive-foreground: 210 40% 98%;\n\n    --border: 217.2 32.6% 17.5%;\n    --input: 217.2 32.6% 17.5%;\n    --ring: 212.7 26.8% 83.9%;\n  }\n}\n\n@layer base {\n  * {\n    @apply border-border;\n  }\n  body {\n    @apply bg-background text-foreground;\n  }\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/pages/index.tsx",
    "content": "import CustomLink from \"@/components/custom-link\"\n\nexport default function Index() {\n  return (\n    <div className=\"mx-auto mt-10 max-w-screen-md space-y-4\">\n      <h1 className=\"text-3xl font-bold\">NextAuth.js Example</h1>\n      <p className=\"leading-loose\">\n        This is an example site to demonstrate how to use{\" \"}\n        <CustomLink href=\"https://nextjs.authjs.dev\">NextAuth.js v5</CustomLink>{\" \"}\n        for authentication with Next.js and the{\" \"}\n        <CustomLink href=\"https://nextjs.org/docs/pages\">\n          Pages Router\n        </CustomLink>\n        . Check out the{\" \"}\n        <CustomLink href=\"/server-example\" className=\"underline\">\n          Server\n        </CustomLink>{\" \"}\n        and the{\" \"}\n        <CustomLink href=\"/client-example\" className=\"underline\">\n          Client\n        </CustomLink>{\" \"}\n        examples to see how to secure pages and get session data.\n      </p>\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/pages/middleware-example/index.tsx",
    "content": "import CustomLink from \"@/components/custom-link\"\n\nexport default function Page() {\n  return (\n    <div className=\"mx-auto mt-10 max-w-screen-md space-y-4\">\n      <h1 className=\"text-3xl font-bold\">Middleware usage</h1>\n      <p className=\"leading-loose\">\n        This page is protected by using the universal{\" \"}\n        <CustomLink href=\"https://nextjs.authjs.dev#auth\">\n          <code>auth()</code>\n        </CustomLink>{\" \"}\n        method in{\" \"}\n        <CustomLink href=\"https://nextjs.org/docs/pages/building-your-application/routing/middleware\">\n          Next.js Middleware\n        </CustomLink>\n        .\n      </p>\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/pages/policy/index.tsx",
    "content": "export default function PolicyPage() {\n  return (\n    <div className=\"mx-auto mt-10 max-w-screen-md space-y-4\">\n      <section>\n        <h2 className=\"text-xl font-bold\">Terms of Service</h2>\n        <p>\n          THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n          EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n          MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n          IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n          CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n          TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n          SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n        </p>\n      </section>\n      <section>\n        <h2 className=\"text-xl font-bold\">Privacy Policy</h2>\n        <p>\n          This site uses JSON Web Tokens and an in-memory database which resets\n          every ~2 hours.\n        </p>\n        <p>\n          Data provided to this site is exclusively used to support signing in\n          and is not passed to any third party services, other than via SMTP or\n          OAuth for the purposes of authentication.\n        </p>\n      </section>\n    </div>\n  )\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/pages/server-example/index.tsx",
    "content": "import CustomLink from \"@/components/custom-link\"\nimport SessionData from \"@/components/session-data\"\n// import { auth } from \"../../auth\"\n// import { getSession } from \"next-auth/react\"\nimport type { Session } from \"next-auth\"\nimport type { GetServerSidePropsContext } from \"next\"\nimport type { InferGetServerSidePropsType, GetServerSideProps } from \"next\"\n\nexport default function Page({\n  serverSession: session,\n}: InferGetServerSidePropsType<typeof getServerSideProps>) {\n  return (\n    <div className=\"mx-auto mt-10 max-w-screen-md space-y-4\">\n      <h1 className=\"text-3xl font-bold\">\n        <code>getServerSideProps</code> Usage\n      </h1>\n      <p className=\"leading-loose\">\n        This page is server-rendered server-side using{\" \"}\n        <CustomLink href=\"https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props\">\n          `getServerSideProps`\n        </CustomLink>\n        .\n      </p>\n      <SessionData session={session} />\n    </div>\n  )\n}\n\nexport const getServerSideProps = (async (\n  context: GetServerSidePropsContext\n) => {\n  // const session = await getSession()\n  const url = `${context.req.headers[\"x-forwarded-proto\"]}://${context.req.headers.host}/api/auth/session`\n\n  // TODO: Test while working on other methods\n  const sessionRes = await fetch(url, {\n    headers: new Headers(context.req.headers as Record<string, string>),\n  })\n  const serverSession: Session = await sessionRes.json()\n  return { props: { serverSession } }\n}) as GetServerSideProps<{ serverSession: Session }>\n"
  },
  {
    "path": "apps/examples/nextjs-pages/postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  darkMode: [\"class\"],\n  content: [\n    \"./pages/**/*.{ts,tsx}\",\n    \"./components/**/*.{ts,tsx}\",\n    \"./app/**/*.{ts,tsx}\",\n    \"./src/**/*.{ts,tsx}\",\n  ],\n  theme: {\n    container: {\n      center: true,\n      padding: \"2rem\",\n      screens: {\n        \"2xl\": \"1400px\",\n      },\n    },\n    extend: {\n      colors: {\n        border: \"hsl(var(--border))\",\n        input: \"hsl(var(--input))\",\n        ring: \"hsl(var(--ring))\",\n        background: \"hsl(var(--background))\",\n        foreground: \"hsl(var(--foreground))\",\n        primary: {\n          DEFAULT: \"hsl(var(--primary))\",\n          foreground: \"hsl(var(--primary-foreground))\",\n        },\n        secondary: {\n          DEFAULT: \"hsl(var(--secondary))\",\n          foreground: \"hsl(var(--secondary-foreground))\",\n        },\n        destructive: {\n          DEFAULT: \"hsl(var(--destructive))\",\n          foreground: \"hsl(var(--destructive-foreground))\",\n        },\n        muted: {\n          DEFAULT: \"hsl(var(--muted))\",\n          foreground: \"hsl(var(--muted-foreground))\",\n        },\n        accent: {\n          DEFAULT: \"hsl(var(--accent))\",\n          foreground: \"hsl(var(--accent-foreground))\",\n        },\n        popover: {\n          DEFAULT: \"hsl(var(--popover))\",\n          foreground: \"hsl(var(--popover-foreground))\",\n        },\n        card: {\n          DEFAULT: \"hsl(var(--card))\",\n          foreground: \"hsl(var(--card-foreground))\",\n        },\n      },\n      borderRadius: {\n        lg: \"var(--radius)\",\n        md: \"calc(var(--radius) - 2px)\",\n        sm: \"calc(var(--radius) - 4px)\",\n      },\n      keyframes: {\n        \"accordion-down\": {\n          from: { height: 0 },\n          to: { height: \"var(--radix-accordion-content-height)\" },\n        },\n        \"accordion-up\": {\n          from: { height: \"var(--radix-accordion-content-height)\" },\n          to: { height: 0 },\n        },\n      },\n      animation: {\n        \"accordion-down\": \"accordion-down 0.2s ease-out\",\n        \"accordion-up\": \"accordion-up 0.2s ease-out\",\n      },\n    },\n  },\n  plugins: [require(\"tailwindcss-animate\")],\n}\n"
  },
  {
    "path": "apps/examples/nextjs-pages/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\"./*\"],\n      \"auth\": [\"./auth\"]\n    }\n  },\n  \"include\": [\n    \"process.d.ts\",\n    \"next-env.d.ts\",\n    \"**/*.ts\",\n    \"**/*.tsx\",\n    \".next/types/**/*.ts\"\n  ],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "apps/examples/qwik/.eslintignore",
    "content": "**/*.log\n**/.DS_Store\n*.\n.vscode/settings.json\n.history\n.yarn\nbazel-*\nbazel-bin\nbazel-out\nbazel-qwik\nbazel-testlogs\ndist\ndist-dev\nlib\nlib-types\netc\nexternal\nnode_modules\ntemp\ntsc-out\ntsdoc-metadata.json\ntarget\noutput\nrollup.config.js\nbuild\n.cache\n.vscode\n.rollup.cache\ndist\ntsconfig.tsbuildinfo\nvite.config.ts\n*.spec.tsx\n*.spec.ts\n.netlify\npnpm-lock.yaml\npackage-lock.json\nyarn.lock\nserver\n"
  },
  {
    "path": "apps/examples/qwik/.eslintrc.cjs",
    "content": "module.exports = {\n  root: true,\n  env: {\n    browser: true,\n    es2021: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"plugin:@typescript-eslint/recommended\",\n    \"plugin:qwik/recommended\",\n  ],\n  parser: \"@typescript-eslint/parser\",\n  parserOptions: {\n    tsconfigRootDir: __dirname,\n    project: [\"./tsconfig.json\"],\n    ecmaVersion: 2021,\n    sourceType: \"module\",\n    ecmaFeatures: {\n      jsx: true,\n    },\n  },\n  plugins: [\"@typescript-eslint\"],\n  rules: {\n    \"@typescript-eslint/no-explicit-any\": \"off\",\n    \"@typescript-eslint/explicit-module-boundary-types\": \"off\",\n    \"@typescript-eslint/no-inferrable-types\": \"off\",\n    \"@typescript-eslint/no-non-null-assertion\": \"off\",\n    \"@typescript-eslint/no-empty-interface\": \"off\",\n    \"@typescript-eslint/no-namespace\": \"off\",\n    \"@typescript-eslint/no-empty-function\": \"off\",\n    \"@typescript-eslint/no-this-alias\": \"off\",\n    \"@typescript-eslint/ban-types\": \"off\",\n    \"@typescript-eslint/ban-ts-comment\": \"off\",\n    \"prefer-spread\": \"off\",\n    \"no-case-declarations\": \"off\",\n    \"no-console\": \"off\",\n    \"@typescript-eslint/no-unused-vars\": [\"error\"],\n    \"@typescript-eslint/consistent-type-imports\": \"warn\",\n    \"@typescript-eslint/no-unnecessary-condition\": \"warn\",\n  },\n};\n"
  },
  {
    "path": "apps/examples/qwik/.gitignore",
    "content": "# Build\n/dist\n/lib\n/lib-types\n/server\n\n# Development\nnode_modules\n*.local\n\n# Cache\n.cache\n.mf\n.rollup.cache\ntsconfig.tsbuildinfo\n\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\n# Editor\n.vscode/*\n!.vscode/launch.json\n!.vscode/*.code-snippets\n\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n\n# Yarn\n.yarn/*\n!.yarn/releases\n\n# Vercel\n.vercel\n"
  },
  {
    "path": "apps/examples/qwik/.prettierignore",
    "content": "**/*.log\n**/.DS_Store\n*.\n.vscode/settings.json\n.history\n.yarn\nbazel-*\nbazel-bin\nbazel-out\nbazel-qwik\nbazel-testlogs\ndist\ndist-dev\nlib\nlib-types\netc\nexternal\nnode_modules\ntemp\ntsc-out\ntsdoc-metadata.json\ntarget\noutput\nrollup.config.js\nbuild\n.cache\n.vscode\n.rollup.cache\ntsconfig.tsbuildinfo\nvite.config.ts\n*.spec.tsx\n*.spec.ts\n.netlify\npnpm-lock.yaml\npackage-lock.json\nyarn.lock\nserver\n"
  },
  {
    "path": "apps/examples/qwik/.prettierrc.js",
    "content": "export default {\n  plugins: [\"prettier-plugin-tailwindcss\"],\n};\n"
  },
  {
    "path": "apps/examples/qwik/.vscode/launch.json",
    "content": "{\n  // Use IntelliSense to learn about possible attributes.\n  // Hover to view descriptions of existing attributes.\n  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"name\": \"Launch Chrome\",\n      \"request\": \"launch\",\n      \"type\": \"chrome\",\n      \"url\": \"http://localhost:5173\",\n      \"webRoot\": \"${workspaceFolder}\"\n    },\n    {\n      \"type\": \"node\",\n      \"name\": \"dev.debug\",\n      \"request\": \"launch\",\n      \"skipFiles\": [\"<node_internals>/**\"],\n      \"cwd\": \"${workspaceFolder}\",\n      \"program\": \"${workspaceFolder}/node_modules/vite/bin/vite.js\",\n      \"args\": [\"--mode\", \"ssr\", \"--force\"]\n    }\n  ]\n}\n"
  },
  {
    "path": "apps/examples/qwik/.vscode/qwik-city.code-snippets",
    "content": "{\n  \"onRequest\": {\n    \"scope\": \"javascriptreact,typescriptreact\",\n    \"prefix\": \"qonRequest\",\n    \"description\": \"onRequest function for a route index\",\n    \"body\": [\n      \"export const onRequest: RequestHandler = (request) => {\",\n      \"  $0\",\n      \"};\",\n    ],\n  },\n  \"loader$\": {\n    \"scope\": \"javascriptreact,typescriptreact\",\n    \"prefix\": \"qloader$\",\n    \"description\": \"loader$()\",\n    \"body\": [\"export const $1 = routeLoader$(() => {\", \"  $0\", \"});\"],\n  },\n  \"action$\": {\n    \"scope\": \"javascriptreact,typescriptreact\",\n    \"prefix\": \"qaction$\",\n    \"description\": \"action$()\",\n    \"body\": [\"export const $1 = routeAction$((data) => {\", \"  $0\", \"});\"],\n  },\n  \"Full Page\": {\n    \"scope\": \"javascriptreact,typescriptreact\",\n    \"prefix\": \"qpage\",\n    \"description\": \"Simple page component\",\n    \"body\": [\n      \"import { component$ } from '@builder.io/qwik';\",\n      \"\",\n      \"export default component$(() => {\",\n      \"  $0\",\n      \"});\",\n    ],\n  },\n}\n"
  },
  {
    "path": "apps/examples/qwik/.vscode/qwik.code-snippets",
    "content": "{\n  \"Qwik component (simple)\": {\n    \"scope\": \"javascriptreact,typescriptreact\",\n    \"prefix\": \"qcomponent$\",\n    \"description\": \"Simple Qwik component\",\n    \"body\": [\n      \"export const ${1:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}} = component$(() => {\",\n      \"  return <${2:div}>$4</$2>\",\n      \"});\",\n    ],\n  },\n  \"Qwik component (props)\": {\n    \"scope\": \"typescriptreact\",\n    \"prefix\": \"qcomponent$ + props\",\n    \"description\": \"Qwik component w/ props\",\n    \"body\": [\n      \"export interface ${1:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}}Props {\",\n      \"  $2\",\n      \"}\",\n      \"\",\n      \"export const $1 = component$<$1Props>((props) => {\",\n      \"  const ${2:count} = useSignal(0);\",\n      \"  return (\",\n      \"    <${3:div} on${4:Click}$={(ev) => {$5}}>\",\n      \"      $6\",\n      \"    </${3}>\",\n      \"  );\",\n      \"});\",\n    ],\n  },\n  \"Qwik signal\": {\n    \"scope\": \"javascriptreact,typescriptreact\",\n    \"prefix\": \"quseSignal\",\n    \"description\": \"useSignal() declaration\",\n    \"body\": [\"const ${1:foo} = useSignal($2);\", \"$0\"],\n  },\n  \"Qwik store\": {\n    \"scope\": \"javascriptreact,typescriptreact\",\n    \"prefix\": \"quseStore\",\n    \"description\": \"useStore() declaration\",\n    \"body\": [\"const ${1:state} = useStore({\", \"  $2\", \"});\", \"$0\"],\n  },\n  \"$ hook\": {\n    \"scope\": \"javascriptreact,typescriptreact\",\n    \"prefix\": \"q$\",\n    \"description\": \"$() function hook\",\n    \"body\": [\"$(() => {\", \"  $0\", \"});\", \"\"],\n  },\n  \"useVisibleTask\": {\n    \"scope\": \"javascriptreact,typescriptreact\",\n    \"prefix\": \"quseVisibleTask\",\n    \"description\": \"useVisibleTask$() function hook\",\n    \"body\": [\"useVisibleTask$(({ track }) => {\", \"  $0\", \"});\", \"\"],\n  },\n  \"useTask\": {\n    \"scope\": \"javascriptreact,typescriptreact\",\n    \"prefix\": \"quseTask$\",\n    \"description\": \"useTask$() function hook\",\n    \"body\": [\n      \"useTask$(({ track }) => {\",\n      \"  track(() => $1);\",\n      \"  $0\",\n      \"});\",\n      \"\",\n    ],\n  },\n  \"useResource\": {\n    \"scope\": \"javascriptreact,typescriptreact\",\n    \"prefix\": \"quseResource$\",\n    \"description\": \"useResource$() declaration\",\n    \"body\": [\n      \"const $1 = useResource$(({ track, cleanup }) => {\",\n      \"  $0\",\n      \"});\",\n      \"\",\n    ],\n  },\n}\n"
  },
  {
    "path": "apps/examples/qwik/README.md",
    "content": "# Qwik City App ⚡️\n\n- [Qwik Docs](https://qwik.dev/)\n- [Discord](https://qwik.dev/chat)\n- [Qwik GitHub](https://github.com/QwikDev/qwik)\n- [@QwikDev](https://twitter.com/QwikDev)\n- [Vite](https://vitejs.dev/)\n\n---\n\n## Project Structure\n\nThis project is using Qwik with [QwikCity](https://qwik.dev/qwikcity/overview/). QwikCity is just an extra set of tools on top of Qwik to make it easier to build a full site, including directory-based routing, layouts, and more.\n\nInside your project, you'll see the following directory structure:\n\n```\n├── public/\n│   └── ...\n└── src/\n    ├── components/\n    │   └── ...\n    └── routes/\n        └── ...\n```\n\n- `src/routes`: Provides the directory-based routing, which can include a hierarchy of `layout.tsx` layout files, and an `index.tsx` file as the page. Additionally, `index.ts` files are endpoints. Please see the [routing docs](https://qwik.dev/qwikcity/routing/overview/) for more info.\n\n- `src/components`: Recommended directory for components.\n\n- `public`: Any static assets, like images, can be placed in the public directory. Please see the [Vite public directory](https://vitejs.dev/guide/assets.html#the-public-directory) for more info.\n\n## Add Integrations and deployment\n\nUse the `pnpm qwik add` command to add additional integrations. Some examples of integrations includes: Cloudflare, Netlify or Express Server, and the [Static Site Generator (SSG)](https://qwik.dev/qwikcity/guides/static-site-generation/).\n\n```shell\npnpm qwik add # or `pnpm qwik add`\n```\n\n## Development\n\nDevelopment mode uses [Vite's development server](https://vitejs.dev/). The `dev` command will server-side render (SSR) the output during development.\n\n```shell\nnpm start # or `pnpm start`\n```\n\n> Note: during dev mode, Vite may request a significant number of `.js` files. This does not represent a Qwik production build.\n\n## Preview\n\nThe preview command will create a production build of the client modules, a production build of `src/entry.preview.tsx`, and run a local server. The preview server is only for convenience to preview a production build locally and should not be used as a production server.\n\n```shell\npnpm preview # or `pnpm preview`\n```\n\n## Production\n\nThe production build will generate client and server modules by running both client and server build commands. The build command will use TypeScript to run a type check on the source code.\n\n```shell\npnpm build # or `pnpm build`\n```\n"
  },
  {
    "path": "apps/examples/qwik/adapters/vercel-edge/vite.config.ts",
    "content": "import { vercelEdgeAdapter } from \"@builder.io/qwik-city/adapters/vercel-edge/vite\";\nimport { extendConfig } from \"@builder.io/qwik-city/vite\";\nimport baseConfig from \"../../vite.config\";\n\nexport default extendConfig(baseConfig, () => {\n  return {\n    build: {\n      ssr: true,\n      rollupOptions: {\n        input: [\"src/entry.vercel-edge.tsx\", \"@qwik-city-plan\"],\n      },\n      outDir: \".vercel/output/functions/_qwik-city.func\",\n    },\n    plugins: [vercelEdgeAdapter()],\n  };\n});\n"
  },
  {
    "path": "apps/examples/qwik/package.json",
    "content": "{\n  \"name\": \"qwik-example-app\",\n  \"description\": \"An example project for Auth.js with Qwik\",\n  \"engines\": {\n    \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\n  },\n  \"engines-annotation\": \"Mostly required by sharp which needs a Node-API v9 compatible runtime\",\n  \"private\": true,\n  \"trustedDependencies\": [\n    \"sharp\"\n  ],\n  \"trustedDependencies-annotation\": \"Needed for bun to allow running install scripts\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"qwik build\",\n    \"build.client\": \"vite build\",\n    \"build.preview\": \"vite build --ssr src/entry.preview.tsx\",\n    \"build.server\": \"vite build -c adapters/vercel-edge/vite.config.ts\",\n    \"build.types\": \"tsc --incremental --noEmit\",\n    \"deploy\": \"vercel deploy\",\n    \"dev\": \"vite --mode ssr\",\n    \"dev.debug\": \"node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force\",\n    \"fmt\": \"prettier --write .\",\n    \"fmt.check\": \"prettier --check .\",\n    \"lint\": \"eslint \\\"src/**/*.ts*\\\"\",\n    \"preview\": \"qwik build preview && vite preview --open\",\n    \"start\": \"vite --open --mode ssr\",\n    \"qwik\": \"qwik\"\n  },\n  \"devDependencies\": {\n    \"@auth/qwik\": \"latest\",\n    \"@builder.io/qwik\": \"^1.5.7\",\n    \"@builder.io/qwik-city\": \"^1.5.7\",\n    \"@types/eslint\": \"^8.56.10\",\n    \"@types/node\": \"^20.12.7\",\n    \"@typescript-eslint/eslint-plugin\": \"^7.7.1\",\n    \"@typescript-eslint/parser\": \"^7.7.1\",\n    \"autoprefixer\": \"^10.4.14\",\n    \"eslint\": \"^8.57.0\",\n    \"eslint-plugin-qwik\": \"^1.5.7\",\n    \"postcss\": \"^8.4.31\",\n    \"prettier\": \"^3.2.5\",\n    \"prettier-plugin-tailwindcss\": \"^0.5.4\",\n    \"tailwindcss\": \"3.3.3\",\n    \"typescript\": \"5.4.5\",\n    \"undici\": \"*\",\n    \"vercel\": \"^29.1.1\",\n    \"vite\": \"^5.4.12\",\n    \"vite-tsconfig-paths\": \"^4.2.1\"\n  }\n}\n"
  },
  {
    "path": "apps/examples/qwik/postcss.config.cjs",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n};\n"
  },
  {
    "path": "apps/examples/qwik/public/manifest.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/web-manifest-combined.json\",\n  \"name\": \"qwik-project-name\",\n  \"short_name\": \"Welcome to Qwik\",\n  \"start_url\": \".\",\n  \"display\": \"standalone\",\n  \"background_color\": \"#fff\",\n  \"description\": \"A Qwik project app.\"\n}\n"
  },
  {
    "path": "apps/examples/qwik/public/robots.txt",
    "content": ""
  },
  {
    "path": "apps/examples/qwik/qwik.env.d.ts",
    "content": "// This file can be used to add references for global types like `vite/client`.\n\n// Add global `vite/client` types. For more info, see: https://vitejs.dev/guide/features#client-types\n/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "apps/examples/qwik/src/components/avatar/avatar.tsx",
    "content": "import { component$ } from \"@builder.io/qwik\";\n\ntype Props = {\n  src: string;\n  alt: string;\n};\n\nexport const Avatar = component$<Props>((props) => {\n  return (\n    <img\n      src={props.src}\n      alt={props.alt}\n      class=\"overflow-hidden rounded-full border border-slate-300 leading-[0px]\"\n      width=\"40\"\n      height=\"40\"\n    />\n  );\n});\n"
  },
  {
    "path": "apps/examples/qwik/src/components/header/header.tsx",
    "content": "import { component$ } from \"@builder.io/qwik\";\nimport { Link } from \"@builder.io/qwik-city\";\nimport { useSession, useSignIn, useSignOut } from \"~/routes/plugin@auth\";\nimport { QwikIcon } from \"../icones/qwik\";\nimport { Avatar } from \"../avatar/avatar\";\n\nexport const Header = component$(() => {\n  const session = useSession();\n  const signInSig = useSignIn();\n  const signOutSig = useSignOut();\n\n  return (\n    <header class=\"flex h-20 items-center gap-3 border-b px-6 py-3\">\n      <Link href=\"/\">\n        <QwikIcon width=\"46\" height=\"50\" />\n      </Link>\n      <span class=\"text-xl font-bold text-slate-800\">Auth.js with Qwik</span>\n\n      {session.value?.user ? (\n        <div class=\"ml-auto flex items-center justify-center gap-8\">\n          <Avatar\n            src={session.value.user.image ?? \"\"}\n            alt={session.value.user.name ?? \"\"}\n          />\n          <Link\n            class=\"cursor-pointer text-xl font-bold text-slate-800\"\n            onClick$={() => signOutSig.submit({ redirectTo: \"/\" })}\n          >\n            Logout\n          </Link>\n        </div>\n      ) : (\n        <div class=\"ml-auto flex items-center justify-center gap-8\">\n          <Link\n            class=\"cursor-pointer text-xl font-bold text-slate-800\"\n            onClick$={() => signInSig.submit({ redirectTo: \"/\" })}\n          >\n            SignIn\n          </Link>\n        </div>\n      )}\n    </header>\n  );\n});\n"
  },
  {
    "path": "apps/examples/qwik/src/components/icones/qwik.tsx",
    "content": "import { type QwikIntrinsicElements } from \"@builder.io/qwik\";\n\nexport const QwikIcon = function FluentAnimalTurtle24Regular(\n  props: QwikIntrinsicElements[\"svg\"],\n  key: string,\n) {\n  return (\n    <svg\n      width=\"500\"\n      height=\"506\"\n      viewBox=\"0 0 500 506\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      {...props}\n      key={key}\n    >\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M250 449.707L431.102 505.511L343.037 423.652L129.174 224.859L179.178 174.86L156.157 16.117L8.34822 193.702C-2.78296 212.982 -2.78273 236.736 8.34883 256.016L102.191 418.551C113.323 437.831 133.894 449.707 156.156 449.707L250 449.707Z\"\n        fill=\"#18B6F6\"\n      />\n      <path\n        d=\"M343.843 0L156.157 1.74069e-05C133.894 1.94717e-05 113.323 11.8771 102.192 31.1573L8.34822 193.702L156.157 16.117L370.826 224.859L330.828 264.86L343.037 423.652L431.102 505.511C436.18 507.075 440.635 501.755 438.204 497.031L397.809 418.551L491.651 256.016C502.783 236.736 502.783 212.982 491.652 193.702L397.808 31.1572C386.677 11.8771 366.106 -2.06475e-06 343.843 0Z\"\n        fill=\"#AC7EF4\"\n      />\n      <path\n        d=\"M370.826 224.859L156.157 16.117L179.178 174.86L129.174 224.859L343.037 423.652L330.828 264.86L370.826 224.859Z\"\n        fill=\"white\"\n      />\n    </svg>\n  );\n};\n"
  },
  {
    "path": "apps/examples/qwik/src/components/router-head/router-head.tsx",
    "content": "import { component$ } from \"@builder.io/qwik\";\nimport { useDocumentHead, useLocation } from \"@builder.io/qwik-city\";\n\n/**\n * The RouterHead component is placed inside of the document `<head>` element.\n */\nexport const RouterHead = component$(() => {\n  const head = useDocumentHead();\n  const loc = useLocation();\n\n  return (\n    <>\n      <title>{head.title}</title>\n\n      <link rel=\"canonical\" href={loc.url.href} />\n      <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n      <link rel=\"icon\" type=\"image/svg+xml\" href=\"/favicon.svg\" />\n\n      {head.meta.map((m) => (\n        <meta key={m.key} {...m} />\n      ))}\n\n      {head.links.map((l) => (\n        <link key={l.key} {...l} />\n      ))}\n\n      {head.styles.map((s) => (\n        <style\n          key={s.key}\n          {...s.props}\n          {...(s.props?.dangerouslySetInnerHTML\n            ? {}\n            : { dangerouslySetInnerHTML: s.style })}\n        />\n      ))}\n\n      {head.scripts.map((s) => (\n        <script\n          key={s.key}\n          {...s.props}\n          {...(s.props?.dangerouslySetInnerHTML\n            ? {}\n            : { dangerouslySetInnerHTML: s.script })}\n        />\n      ))}\n    </>\n  );\n});\n"
  },
  {
    "path": "apps/examples/qwik/src/entry.dev.tsx",
    "content": "/*\n * WHAT IS THIS FILE?\n *\n * Development entry point using only client-side modules:\n * - Do not use this mode in production!\n * - No SSR\n * - No portion of the application is pre-rendered on the server.\n * - All of the application is running eagerly in the browser.\n * - More code is transferred to the browser than in SSR mode.\n * - Optimizer/Serialization/Deserialization code is not exercised!\n */\nimport { render, type RenderOptions } from \"@builder.io/qwik\";\nimport Root from \"./root\";\n\nexport default function (opts: RenderOptions) {\n  return render(document, <Root />, opts);\n}\n"
  },
  {
    "path": "apps/examples/qwik/src/entry.preview.tsx",
    "content": "/*\n * WHAT IS THIS FILE?\n *\n * It's the bundle entry point for `npm run preview`.\n * That is, serving your app built in production mode.\n *\n * Feel free to modify this file, but don't remove it!\n *\n * Learn more about Vite's preview command:\n * - https://vitejs.dev/config/preview-options.html#preview-options\n *\n */\nimport { createQwikCity } from \"@builder.io/qwik-city/middleware/node\";\nimport qwikCityPlan from \"@qwik-city-plan\";\n// make sure qwikCityPlan is imported before entry\nimport render from \"./entry.ssr\";\n\n/**\n * The default export is the QwikCity adapter used by Vite preview.\n */\nexport default createQwikCity({ render, qwikCityPlan });\n"
  },
  {
    "path": "apps/examples/qwik/src/entry.ssr.tsx",
    "content": "/**\n * WHAT IS THIS FILE?\n *\n * SSR entry point, in all cases the application is rendered outside the browser, this\n * entry point will be the common one.\n *\n * - Server (express, cloudflare...)\n * - npm run start\n * - npm run preview\n * - npm run build\n *\n */\nimport {\n  renderToStream,\n  type RenderToStreamOptions,\n} from \"@builder.io/qwik/server\";\nimport { manifest } from \"@qwik-client-manifest\";\nimport Root from \"./root\";\n\nexport default function (opts: RenderToStreamOptions) {\n  return renderToStream(<Root />, {\n    manifest,\n    ...opts,\n    // Use container attributes to set attributes on the html tag.\n    containerAttributes: {\n      lang: \"en-us\",\n      ...opts.containerAttributes,\n    },\n    serverData: {\n      ...opts.serverData,\n    },\n  });\n}\n"
  },
  {
    "path": "apps/examples/qwik/src/entry.vercel-edge.tsx",
    "content": "/*\n * WHAT IS THIS FILE?\n *\n * It's the entry point for Vercel Edge when building for production.\n *\n * Learn more about the Vercel Edge integration here:\n * - https://qwik.dev/docs/deployments/vercel-edge/\n *\n */\nimport {\n  createQwikCity,\n  type PlatformVercel,\n} from \"@builder.io/qwik-city/middleware/vercel-edge\";\nimport qwikCityPlan from \"@qwik-city-plan\";\nimport { manifest } from \"@qwik-client-manifest\";\nimport render from \"./entry.ssr\";\n\ndeclare global {\n  interface QwikCityPlatform extends PlatformVercel {}\n}\n\nexport default createQwikCity({ render, qwikCityPlan, manifest });\n"
  },
  {
    "path": "apps/examples/qwik/src/global.css",
    "content": "/**\n * Tailwind CSS imports\n * View the full documentation at https://tailwindcss.com\n */\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nhtml,\nbody {\n  background-color: white;\n  height: 100%;\n  width: 100%;\n}\n"
  },
  {
    "path": "apps/examples/qwik/src/root.tsx",
    "content": "import { component$ } from \"@builder.io/qwik\";\nimport {\n  QwikCityProvider,\n  RouterOutlet,\n  ServiceWorkerRegister,\n} from \"@builder.io/qwik-city\";\nimport { RouterHead } from \"./components/router-head/router-head\";\n\nimport \"./global.css\";\n\nexport default component$(() => {\n  /**\n   * The root of a QwikCity site always start with the <QwikCityProvider> component,\n   * immediately followed by the document's <head> and <body>.\n   *\n   * Don't remove the `<head>` and `<body>` elements.\n   */\n\n  return (\n    <QwikCityProvider>\n      <head>\n        <meta charset=\"utf-8\" />\n        <link rel=\"manifest\" href=\"/manifest.json\" />\n        <RouterHead />\n      </head>\n      <body lang=\"en\">\n        <RouterOutlet />\n        <ServiceWorkerRegister />\n      </body>\n    </QwikCityProvider>\n  );\n});\n"
  },
  {
    "path": "apps/examples/qwik/src/routes/index.tsx",
    "content": "import { component$ } from \"@builder.io/qwik\";\nimport { useSession } from \"./plugin@auth\";\n\nexport default component$(() => {\n  const session = useSession();\n  return (\n    <div class=\"h-[calc(100vh-5rem)] w-screen bg-slate-100 pt-40 text-center text-2xl\">\n      <div>This is an example site to demonstrate how to use</div>\n      <div class=\"py-2 font-bold text-blue-600\">\n        <a target=\"_blank\" href=\"https://authjs.dev/\">\n          Auth.js\n        </a>\n      </div>\n      <div>for authentication with</div>\n      <div class=\"py-2 font-bold text-blue-600\">\n        <a target=\"_blank\" href=\"https://qwik.dev/\">\n          Qwik\n        </a>\n      </div>\n      {session.value?.user && (\n        <>\n          <div class=\"pt-6 text-2xl font-bold text-slate-800\">\n            You are logged. Now you can visit\n          </div>\n          <a class=\"py-4 font-bold text-blue-600\" href=\"/protected\">\n            this protected route.\n          </a>\n        </>\n      )}\n    </div>\n  );\n});\n"
  },
  {
    "path": "apps/examples/qwik/src/routes/layout.tsx",
    "content": "import { component$, Slot } from \"@builder.io/qwik\";\nimport type { DocumentHead, RequestHandler } from \"@builder.io/qwik-city\";\nimport { Header } from \"~/components/header/header\";\n\nexport const onGet: RequestHandler = async ({ cacheControl }) => {\n  // Control caching for this request for best performance and to reduce hosting costs:\n  // https://qwik.dev/docs/caching/\n  cacheControl({\n    // Always serve a cached response by default, up to a week stale\n    staleWhileRevalidate: 60 * 60 * 24 * 7,\n    // Max once every 5 seconds, revalidate on the server to get a fresh version of this page\n    maxAge: 5,\n  });\n};\n\nexport default component$(() => {\n  return (\n    <div>\n      <Header />\n      <Slot />\n    </div>\n  );\n});\n\nexport const head: DocumentHead = {\n  title: \"Auth.js with Qwik\",\n  meta: [\n    {\n      name: \"description\",\n      content: \"An example project for Auth.js with Qwik\",\n    },\n  ],\n};\n"
  },
  {
    "path": "apps/examples/qwik/src/routes/plugin@auth.ts",
    "content": "import { QwikAuth$ } from \"@auth/qwik\";\nimport GitHub from \"@auth/qwik/providers/github\";\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [GitHub],\n  }),\n);\n"
  },
  {
    "path": "apps/examples/qwik/src/routes/protected/index.tsx",
    "content": "import { component$ } from \"@builder.io/qwik\";\nimport { RequestHandler } from \"@builder.io/qwik-city\";\n\nexport const onRequest: RequestHandler = (event) => {\n  const session = event.sharedMap.get(\"session\");\n  if (!session || new Date(session.expires) < new Date()) {\n    throw event.redirect(302, `/`);\n  }\n};\n\nexport default component$(() => {\n  return (\n    <div class=\"h-[calc(100vh-5rem)] w-screen bg-slate-100 pt-40 text-center text-2xl\">\n      <div class=\"py-4 font-bold text-blue-600\">\n        <a target=\"_blank\" href=\"https://authjs.dev/\">\n          Auth.js is awesome!\n        </a>\n      </div>\n    </div>\n  );\n});\n"
  },
  {
    "path": "apps/examples/qwik/src/routes/service-worker.ts",
    "content": "/*\n * WHAT IS THIS FILE?\n *\n * The service-worker.ts file is used to have state of the art prefetching.\n * https://qwik.dev/qwikcity/prefetching/overview/\n *\n * Qwik uses a service worker to speed up your site and reduce latency, ie, not used in the traditional way of offline.\n * You can also use this file to add more functionality that runs in the service worker.\n */\nimport { setupServiceWorker } from \"@builder.io/qwik-city/service-worker\";\n\nsetupServiceWorker();\n\naddEventListener(\"install\", () => self.skipWaiting());\n\naddEventListener(\"activate\", () => self.clients.claim());\n\ndeclare const self: ServiceWorkerGlobalScope;\n"
  },
  {
    "path": "apps/examples/qwik/tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nexport default {\n  content: [\"./src/**/*.{js,ts,jsx,tsx,mdx}\"],\n  theme: {\n    extend: {},\n  },\n  plugins: [],\n};\n"
  },
  {
    "path": "apps/examples/qwik/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"target\": \"ES2017\",\n    \"module\": \"ES2022\",\n    \"lib\": [\"es2022\", \"DOM\", \"WebWorker\", \"DOM.Iterable\"],\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"@builder.io/qwik\",\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"resolveJsonModule\": true,\n    \"moduleResolution\": \"Bundler\",\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true,\n    \"incremental\": true,\n    \"isolatedModules\": true,\n    \"outDir\": \"tmp\",\n    \"noEmit\": true,\n    \"paths\": {\n      \"~/*\": [\"./src/*\"]\n    }\n  },\n  \"files\": [\"./.eslintrc.cjs\"],\n  \"include\": [\"src\", \"./*.d.ts\", \"./*.config.ts\"]\n}\n"
  },
  {
    "path": "apps/examples/qwik/vercel.json",
    "content": "{\n  \"headers\": [\n    {\n      \"source\": \"/(.*)?service-worker.js\",\n      \"headers\": [\n        {\n          \"key\": \"Cache-Control\",\n          \"value\": \"public, max-age=0, must-revalidate\"\n        }\n      ]\n    },\n    {\n      \"source\": \"/build/(.*)\",\n      \"headers\": [\n        {\n          \"key\": \"Cache-Control\",\n          \"value\": \"public, max-age=31536000, s-maxage=31536000, immutable\"\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "apps/examples/qwik/vite.config.ts",
    "content": "/**\n * This is the base config for vite.\n * When building, the adapter config is used which loads this file and extends it.\n */\nimport { defineConfig, type UserConfig } from \"vite\";\nimport { qwikVite } from \"@builder.io/qwik/optimizer\";\nimport { qwikCity } from \"@builder.io/qwik-city/vite\";\nimport tsconfigPaths from \"vite-tsconfig-paths\";\nimport pkg from \"./package.json\";\n\ntype PkgDep = Record<string, string>;\nconst { dependencies = {}, devDependencies = {} } = pkg as any as {\n  dependencies: PkgDep;\n  devDependencies: PkgDep;\n  [key: string]: unknown;\n};\nerrorOnDuplicatesPkgDeps(devDependencies, dependencies);\n\n/**\n * Note that Vite normally starts from `index.html` but the qwikCity plugin makes start at `src/entry.ssr.tsx` instead.\n */\nexport default defineConfig(({ command, mode }): UserConfig => {\n  return {\n    plugins: [qwikCity(), qwikVite(), tsconfigPaths()],\n    // This tells Vite which dependencies to pre-build in dev mode.\n    optimizeDeps: {\n      // Put problematic deps that break bundling here, mostly those with binaries.\n      // For example ['better-sqlite3'] if you use that in server functions.\n      exclude: [],\n    },\n\n    /**\n     * This is an advanced setting. It improves the bundling of your server code. To use it, make sure you understand when your consumed packages are dependencies or dev dependencies. (otherwise things will break in production)\n     */\n    // ssr:\n    //   command === \"build\" && mode === \"production\"\n    //     ? {\n    //         // All dev dependencies should be bundled in the server build\n    //         noExternal: Object.keys(devDependencies),\n    //         // Anything marked as a dependency will not be bundled\n    //         // These should only be production binary deps (including deps of deps), CLI deps, and their module graph\n    //         // If a dep-of-dep needs to be external, add it here\n    //         // For example, if something uses `bcrypt` but you don't have it as a dep, you can write\n    //         // external: [...Object.keys(dependencies), 'bcrypt']\n    //         external: Object.keys(dependencies),\n    //       }\n    //     : undefined,\n\n    server: {\n      headers: {\n        // Don't cache the server response in dev mode\n        \"Cache-Control\": \"public, max-age=0\",\n      },\n    },\n    preview: {\n      headers: {\n        // Do cache the server response in preview (non-adapter production build)\n        \"Cache-Control\": \"public, max-age=600\",\n      },\n    },\n  };\n});\n\n// *** utils ***\n\n/**\n * Function to identify duplicate dependencies and throw an error\n * @param {Object} devDependencies - List of development dependencies\n * @param {Object} dependencies - List of production dependencies\n */\nfunction errorOnDuplicatesPkgDeps(\n  devDependencies: PkgDep,\n  dependencies: PkgDep,\n) {\n  let msg = \"\";\n  // Create an array 'duplicateDeps' by filtering devDependencies.\n  // If a dependency also exists in dependencies, it is considered a duplicate.\n  const duplicateDeps = Object.keys(devDependencies).filter(\n    (dep) => dependencies[dep],\n  );\n\n  // include any known qwik packages\n  const qwikPkg = Object.keys(dependencies).filter((value) =>\n    /qwik/i.test(value),\n  );\n\n  // any errors for missing \"qwik-city-plan\"\n  // [PLUGIN_ERROR]: Invalid module \"@qwik-city-plan\" is not a valid package\n  msg = `Move qwik packages ${qwikPkg.join(\", \")} to devDependencies`;\n\n  if (qwikPkg.length > 0) {\n    throw new Error(msg);\n  }\n\n  // Format the error message with the duplicates list.\n  // The `join` function is used to represent the elements of the 'duplicateDeps' array as a comma-separated string.\n  msg = `\n    Warning: The dependency \"${duplicateDeps.join(\n      \", \",\n    )}\" is listed in both \"devDependencies\" and \"dependencies\".\n    Please move the duplicated dependencies to \"devDependencies\" only and remove it from \"dependencies\"\n  `;\n\n  // Throw an error with the constructed message.\n  if (duplicateDeps.length > 0) {\n    throw new Error(msg);\n  }\n}\n"
  },
  {
    "path": "apps/examples/solid-start/.gitignore",
    "content": "dist\n.solid\n.output\n.vercel\n.netlify\nnetlify\n\n# dependencies\n/node_modules\n\n# IDEs and editors\n/.idea\n.project\n.classpath\n*.launch\n.settings/\n\n# Temp\ngitignore\n\n# System Files\n.DS_Store\nThumbs.db\n\n.env\n\n.vercel\n"
  },
  {
    "path": "apps/examples/solid-start/README.md",
    "content": "> The example repository is maintained from a [monorepo](https://github.com/nextauthjs/next-auth/tree/main/apps/examples/solid-start). Pull Requests should be opened against [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth).\n\n<p align=\"center\">\n   <br/>\n   <a href=\"https://authjs.dev\" target=\"_blank\">\n   <img height=\"64\" src=\"https://authjs.dev/img/logo-sm.png\" />\n   </a>\n   <a href=\"https://start.solidjs.com\" target=\"_blank\">\n   <img height=\"64\" src=\"https://raw.githubusercontent.com/nextauthjs/next-auth/main/docs/public/img/etc/solidstart.svg\" />\n   </a>\n   <h3 align=\"center\"><b>SolidStart Auth</b> - Example App</h3>\n   <p align=\"center\">\n   Open Source. Full Stack. Own Your Data.\n   </p>\n   <p align=\"center\" style=\"align: center;\">\n      <a href=\"https://npm.im/@auth/solid-start\">\n        <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/solid-start?color=green&label=@auth/solid-start&style=flat-square\">\n      </a>\n      <a href=\"https://bundlephobia.com/result?p=@auth/solid-start\">\n        <img src=\"https://img.shields.io/bundlephobia/minzip/@auth/solid-start?label=size&style=flat-square\" alt=\"Bundle Size\"/>\n      </a>\n      <a href=\"https://www.npmtrends.com/@auth/solid-start\">\n        <img src=\"https://img.shields.io/npm/dm/@auth/solid-start?label=downloads&style=flat-square\" alt=\"Downloads\" />\n      </a>\n      <a href=\"https://npm.im/@auth/solid-start\">\n        <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n      </a>\n   </p>\n</p>\n\n## Overview\n\nThis is the official SolidStart Auth example for [Auth.js](https://authjs.dev).\n\n## Getting started\n\nYou can follow the guide below, or click the following button to deploy this example to [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=solid-start-auth-example).\n\n[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/nextauthjs/solid-start-auth-example&project-name=solid-start-auth-example&repository-name=solid-start-auth-example)\n\n### Installing\n\n```sh\npnpm add -D solid-start-vercel\n```\n\n```sh\nnpm i -D solid-start-vercel\n```\n\n```sh\nyarn add -D solid-start-vercel\n```\n\n### Adding to Vite config\n\n```ts\nimport solid from \"solid-start/vite\"\nimport dotenv from \"dotenv\"\nimport { defineConfig } from \"vite\"\n// @ts-expect-error no typing\nimport vercel from \"solid-start-vercel\"\n\nexport default defineConfig(() => {\n  dotenv.config()\n  return {\n    plugins: [solid({ ssr: true, adapter: vercel({ edge: false }) })],\n  }\n})\n```\n\n### Environment Variables\n\n- `ENABLE_VC_BUILD`=`1` .\n\n### Finishing up\n\nCreate a GitHub repo and push the code to it, then deploy it to Vercel.\n"
  },
  {
    "path": "apps/examples/solid-start/package.json",
    "content": "{\n  \"name\": \"solid-start-example-app\",\n  \"scripts\": {\n    \"dev\": \"solid-start dev\",\n    \"build\": \"solid-start build\",\n    \"start\": \"solid-start start\",\n    \"lint\": \"eslint --fix \\\"**/*.{ts,tsx,js,jsx}\\\"\"\n  },\n  \"type\": \"module\",\n  \"devDependencies\": {\n    \"autoprefixer\": \"^10.4.13\",\n    \"postcss\": \"^8.4.19\",\n    \"solid-start-node\": \"^0.2.9\",\n    \"solid-start-vercel\": \"^0.2.9\",\n    \"tailwindcss\": \"^3.2.4\",\n    \"typescript\": \"5.2.2\",\n    \"vite\": \"^4.5.6\"\n  },\n  \"dependencies\": {\n    \"@auth/solid-start\": \"latest\",\n    \"@solidjs/meta\": \"^0.28.0\",\n    \"@solidjs/router\": \"^0.6.0\",\n    \"solid-js\": \"^1.5.7\",\n    \"solid-start\": \"^0.2.9\",\n    \"zod\": \"^3.19.1\"\n  },\n  \"engines\": {\n    \"node\": \"^18.17.0 || ^20.3.0 || >=21.0.0\"\n  }\n}\n"
  },
  {
    "path": "apps/examples/solid-start/postcss.config.cjs",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "apps/examples/solid-start/src/components/NavBar/NavBar.tsx",
    "content": "import { Show, type Component } from \"solid-js\"\nimport { createServerData$ } from \"solid-start/server\"\nimport { authOpts } from \"~/routes/api/auth/[...solidauth]\"\nimport { signIn, signOut } from \"@auth/solid-start/client\"\nimport { getSession } from \"@auth/solid-start\"\nimport { A } from \"solid-start\"\n\ninterface INavBarProps {}\n\nconst NavBar: Component<INavBarProps> = () => {\n  const session = useSession()\n  return (\n    <header class=\"fixed left-2/4 right-2/4 flex w-full -translate-x-2/4 flex-col items-center gap-2\">\n      <nav class=\"flex w-[70vw] items-center justify-between rounded-lg bg-[#0000000d] p-5 sm:w-2/4 lg:w-[40%]\">\n        <Show\n          when={session()?.user}\n          keyed\n          fallback={\n            <>\n              <p class=\"text-lg font-semibold\">You are not signed in</p>\n              <button\n                class=\"flex items-center justify-center rounded-lg bg-[#346df1] p-2.5 text-lg font-bold text-white\"\n                onClick={() => signIn(\"github\")}\n              >\n                Sign in\n              </button>\n            </>\n          }\n        >\n          {(us) => (\n            <>\n              <div class=\"flex items-center gap-2\">\n                <Show when={us.image} keyed>\n                  {(im) => <img src={im} class=\"h-12 w-12 rounded-full\" />}\n                </Show>\n                <div class=\"flex flex-col\">\n                  <h3 class=\"text-lg font-bold\">Signed in as</h3>\n                  <p class=\"text-lg font-semibold\">{us.name}</p>\n                </div>\n              </div>\n              <button\n                onClick={() => signOut()}\n                class=\"font-semibold text-[#555] underline\"\n              >\n                Sign out\n              </button>\n            </>\n          )}\n        </Show>\n      </nav>\n      <div class=\"flex items-center gap-2\">\n        <A class=\"font-bold text-blue-500 underline\" href=\"/\">\n          Home\n        </A>\n        <A class=\"font-bold text-blue-500 underline\" href=\"/protected\">\n          Protected\n        </A>\n      </div>\n    </header>\n  )\n}\n\nexport default NavBar\n\nexport const useSession = () => {\n  return createServerData$(\n    async (_, { request }) => {\n      return await getSession(request, authOpts)\n    },\n    { key: () => [\"auth_user\"] }\n  )\n}\n"
  },
  {
    "path": "apps/examples/solid-start/src/components/NavBar/index.ts",
    "content": "export { default } from \"./NavBar\"\n"
  },
  {
    "path": "apps/examples/solid-start/src/components/Protected/Protected.tsx",
    "content": "import { getSession, type Session } from \"@auth/solid-start\"\nimport { Component, Show } from \"solid-js\"\nimport { useRouteData } from \"solid-start\"\nimport { createServerData$, redirect } from \"solid-start/server\"\nimport { authOpts } from \"~/routes/api/auth/[...solidauth]\"\n\nconst Protected = (Comp: IProtectedComponent) => {\n  const routeData = () => {\n    return createServerData$(\n      async (_, event) => {\n        const session = await getSession(event.request, authOpts)\n        if (!session || !session.user) {\n          throw redirect(\"/\")\n        }\n        return session\n      },\n      { key: () => [\"auth_user\"] }\n    )\n  }\n\n  return {\n    routeData,\n    Page: () => {\n      const session = useRouteData<typeof routeData>()\n      return (\n        <Show when={session()} keyed>\n          {(sess) => <Comp {...sess} />}\n        </Show>\n      )\n    },\n  }\n}\n\ntype IProtectedComponent = Component<Session>\n\nexport default Protected\n"
  },
  {
    "path": "apps/examples/solid-start/src/components/Protected/index.ts",
    "content": "export { default } from \"./Protected\"\n"
  },
  {
    "path": "apps/examples/solid-start/src/components/index.ts",
    "content": "export { default as NavBar } from \"./NavBar\"\nexport { default as Protected } from \"./Protected\"\n"
  },
  {
    "path": "apps/examples/solid-start/src/entry-client.tsx",
    "content": "import { mount, StartClient } from \"solid-start/entry-client\"\n\nmount(() => <StartClient />, document)\n"
  },
  {
    "path": "apps/examples/solid-start/src/entry-server.tsx",
    "content": "import {\n  StartServer,\n  createHandler,\n  renderAsync,\n} from \"solid-start/entry-server\"\n\nexport default createHandler(\n  renderAsync((event) => <StartServer event={event} />)\n)\n"
  },
  {
    "path": "apps/examples/solid-start/src/env/client.ts",
    "content": "import type { ZodFormattedError } from \"zod\"\nimport { clientScheme } from \"./schema\"\n\nexport const formatErrors = (\n  errors: ZodFormattedError<Map<string, string>, string>\n) =>\n  Object.entries(errors)\n    .map(([name, value]) => {\n      if (value && \"_errors\" in value)\n        return `${name}: ${value._errors.join(\", \")}\\n`\n    })\n    .filter(Boolean)\n\nconst env = clientScheme.safeParse(import.meta.env)\n\nif (env.success === false) {\n  console.error(\n    \"❌ Invalid environment variables:\\n\",\n    ...formatErrors(env.error.format())\n  )\n  throw new Error(\"Invalid environment variables\")\n}\n\nexport const clientEnv = env.data\n"
  },
  {
    "path": "apps/examples/solid-start/src/env/schema.ts",
    "content": "import { z } from \"zod\"\n\nexport const serverScheme = z.object({\n  NODE_ENV: z\n    .enum([\"development\", \"production\", \"test\"])\n    .default(\"development\"),\n  GITHUB_ID: z.string(),\n  GITHUB_SECRET: z.string(),\n  AUTH_SECRET: z.string(),\n  NEXTAUTH_URL: z.string().optional(),\n})\n\nexport const clientScheme = z.object({\n  MODE: z.enum([\"development\", \"production\", \"test\"]).default(\"development\"),\n})\n"
  },
  {
    "path": "apps/examples/solid-start/src/env/server.ts",
    "content": "import { serverScheme } from \"./schema\"\nimport type { ZodFormattedError } from \"zod\"\n\nexport const formatErrors = (\n  errors: ZodFormattedError<Map<string, string>, string>\n) =>\n  Object.entries(errors)\n    .map(([name, value]) => {\n      if (value && \"_errors\" in value)\n        return `${name}: ${value._errors.join(\", \")}\\n`\n    })\n    .filter(Boolean)\n\nconst env = serverScheme.safeParse(process.env)\n\nif (env.success === false) {\n  console.error(\n    \"❌ Invalid environment variables:\\n\",\n    ...formatErrors(env.error.format())\n  )\n  throw new Error(\"Invalid environment variables\")\n}\n\nexport const serverEnv = env.data\n"
  },
  {
    "path": "apps/examples/solid-start/src/root.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n"
  },
  {
    "path": "apps/examples/solid-start/src/root.tsx",
    "content": "// @refresh reload\nimport \"./root.css\"\nimport { Suspense } from \"solid-js\"\nimport {\n  Body,\n  ErrorBoundary,\n  FileRoutes,\n  Head,\n  Html,\n  Meta,\n  Routes,\n  Scripts,\n  Title,\n} from \"solid-start\"\nimport { NavBar } from \"./components\"\n\nexport default function Root() {\n  return (\n    <Html lang=\"en\">\n      <Head>\n        <Title>Create JD App</Title>\n        <Meta charset=\"utf-8\" />\n        <Meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n      </Head>\n      <Body>\n        <Suspense>\n          <NavBar />\n          <div class=\"px-8 py-44\">\n            <ErrorBoundary>\n              <Routes>\n                <FileRoutes />\n              </Routes>\n            </ErrorBoundary>\n          </div>\n        </Suspense>\n        <Scripts />\n      </Body>\n    </Html>\n  )\n}\n"
  },
  {
    "path": "apps/examples/solid-start/src/routes/api/auth/[...solidauth].ts",
    "content": "import { SolidAuth, type SolidAuthConfig } from \"@auth/solid-start\"\nimport GitHub from \"@auth/solid-start/providers/github\"\nimport { serverEnv } from \"~/env/server\"\n\nexport const authOpts: SolidAuthConfig = {\n  providers: [\n    GitHub({\n      clientId: serverEnv.GITHUB_ID,\n      clientSecret: serverEnv.GITHUB_SECRET,\n    }),\n  ],\n  debug: false,\n}\n\nexport const { GET, POST } = SolidAuth(authOpts)\n"
  },
  {
    "path": "apps/examples/solid-start/src/routes/index.tsx",
    "content": "import { type ParentComponent } from \"solid-js\"\nimport { A, Title, useRouteData } from \"solid-start\"\nimport { createServerData$ } from \"solid-start/server\"\nimport { authOpts } from \"./api/auth/[...solidauth]\"\nimport { getSession } from \"@auth/solid-start\"\n\nexport const routeData = () => {\n  return createServerData$(\n    async (_, { request }) => {\n      return await getSession(request, authOpts)\n    },\n    { key: () => [\"auth_user\"] }\n  )\n}\nconst Home: ParentComponent = () => {\n  const user = useRouteData<typeof routeData>()\n  return (\n    <>\n      <Title>Create JD App</Title>\n      <div class=\"flex flex-col items-center gap-2\">\n        <h1 class=\"text-4xl font-bold\">SolidStart Auth Example</h1>\n        <p class=\"text-md max-w-[40rem] font-semibold\">\n          This is an example site to demonstrate how to use{\" \"}\n          <A\n            href=\"https://start.solidjs.com/getting-started/what-is-solidstart\"\n            class=\"font-bold text-blue-500 underline\"\n          >\n            SolidStart\n          </A>{\" \"}\n          with{\" \"}\n          <A\n            href=\"https://authjs.dev/reference/solidstart\"\n            class=\"font-bold text-blue-500 underline\"\n          >\n            SolidStart Auth\n          </A>{\" \"}\n          for authentication.\n        </p>\n      </div>\n    </>\n  )\n}\n\nexport default Home\n"
  },
  {
    "path": "apps/examples/solid-start/src/routes/protected.tsx",
    "content": "import { Protected } from \"~/components\"\n\nexport const { routeData, Page } = Protected((session) => {\n  return (\n    <main class=\"flex flex-col items-center gap-2\">\n      <h1>This is a protected route</h1>\n    </main>\n  )\n})\n\nexport default Page\n"
  },
  {
    "path": "apps/examples/solid-start/tailwind.config.cjs",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: [\"./src/**/*.{js,ts,jsx,tsx}\"],\n  theme: {\n    extend: {},\n  },\n  plugins: [],\n}\n"
  },
  {
    "path": "apps/examples/solid-start/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowSyntheticDefaultImports\": true,\n    \"esModuleInterop\": true,\n    \"strict\": true,\n    \"target\": \"ESNext\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"node\",\n    \"jsxImportSource\": \"solid-js\",\n    \"jsx\": \"preserve\",\n    \"types\": [\"vite/client\"],\n    \"baseUrl\": \"./\",\n    \"paths\": {\n      \"~/*\": [\"./src/*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "apps/examples/solid-start/vite.config.ts",
    "content": "import solid from \"solid-start/vite\"\nimport { defineConfig } from \"vite\"\n// @ts-expect-error no typings\nimport vercel from \"solid-start-vercel\"\n\nexport default defineConfig(() => {\n  return {\n    plugins: [solid({ ssr: true, adapter: vercel({ edge: false }) })],\n  }\n})\n"
  },
  {
    "path": "apps/examples/sveltekit/.env.example",
    "content": "# Providers for example app\nAUTH_GITHUB_ID=\nAUTH_GITHUB_SECRET=\nAUTH_LINKEDIN_ID=\nAUTH_LINKEDIN_SECRET=\nAUTH_GOOGLE_ID=\nAUTH_GOOGLE_SECRET=\nAUTH_FACEBOOK_ID=\nAUTH_FACEBOOK_SECRET=\nAUTH_TWITTER_ID=\nAUTH_TWITTER_SECRET=\nAUTH_AUTH0_ID=\nAUTH_AUTH0_SECRET=\nAUTH_AUTH0_ISSUER=\nAUTH_DISCORD_ID=\nAUTH_DISCORD_SECRET=\nAUTH_TWITCH_ID=\nAUTH_TWITCH_SECRET=\nAUTH_PINTEREST_ID=\nAUTH_PINTEREST_SECRET=\n\n# On UNIX systems you can use `openssl rand -hex 32` or \n# https://generate-secret.vercel.app/32 to generate a secret.\nAUTH_SECRET=\n"
  },
  {
    "path": "apps/examples/sveltekit/.eslintignore",
    "content": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/package\n.env\n.env.*\n!.env.example\n\n# Ignore files for PNPM, NPM and YARN\npnpm-lock.yaml\npackage-lock.json\nyarn.lock\n"
  },
  {
    "path": "apps/examples/sveltekit/.eslintrc.cjs",
    "content": "module.exports = {\n  root: true,\n  parser: \"@typescript-eslint/parser\",\n  extends: [\n    \"eslint:recommended\",\n    \"plugin:@typescript-eslint/recommended\",\n    \"prettier\",\n  ],\n  plugins: [\"svelte3\", \"@typescript-eslint\"],\n  ignorePatterns: [\"*.cjs\"],\n  overrides: [{ files: [\"*.svelte\"], processor: \"svelte3/svelte3\" }],\n  settings: {\n    \"svelte3/typescript\": () => require(\"typescript\"),\n  },\n  parserOptions: {\n    sourceType: \"module\",\n    ecmaVersion: 2020,\n  },\n  env: {\n    browser: true,\n    es2017: true,\n    node: true,\n  },\n}\n"
  },
  {
    "path": "apps/examples/sveltekit/.gitignore",
    "content": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/package\n.env\n.env.*\n!.env.example\n.vercel\n.output\nvite.config.js.timestamp-*\nvite.config.ts.timestamp-*\n"
  },
  {
    "path": "apps/examples/sveltekit/.prettierignore",
    "content": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/package\n.env\n.env.*\n!.env.example\n\n# Ignore files for PNPM, NPM and YARN\npnpm-lock.yaml\npackage-lock.json\nyarn.lock\n"
  },
  {
    "path": "apps/examples/sveltekit/.prettierrc",
    "content": "{\n  \"semi\": false,\n  \"plugins\": [\"prettier-plugin-svelte\"],\n  \"overrides\": [{ \"files\": \"*.svelte\", \"options\": { \"parser\": \"svelte\" } }]\n}\n"
  },
  {
    "path": "apps/examples/sveltekit/README.md",
    "content": "> The example repository is maintained from a [monorepo](https://github.com/nextauthjs/next-auth/tree/main/apps/examples/sveltekit). Pull Requests should be opened against [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth).\n\n<p align=\"center\">\n   <br/>\n   <a href=\"https://authjs.dev\" target=\"_blank\">\n   <img height=\"64\" src=\"https://authjs.dev/img/logo-sm.png\" />\n   </a>\n   <a href=\"https://kit.svelte.dev\" target=\"_blank\">\n   <img height=\"64\" src=\"https://upload.wikimedia.org/wikipedia/commons/1/1b/Svelte_Logo.svg\" />\n   </a>\n   <h3 align=\"center\"><b>SvelteKit Auth</b> - Example App</h3>\n   <p align=\"center\">\n   Open Source. Full Stack. Own Your Data.\n   </p>\n   <p align=\"center\" style=\"align: center;\">\n      <a href=\"https://npm.im/@auth/sveltekit\">\n        <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/sveltekit?color=green&label=@auth/sveltekit&style=flat-square\">\n      </a>\n      <a href=\"https://bundlephobia.com/result?p=@auth/sveltekit\">\n        <img src=\"https://img.shields.io/bundlephobia/minzip/@auth/sveltekit?label=size&style=flat-square\" alt=\"Bundle Size\"/>\n      </a>\n      <a href=\"https://www.npmtrends.com/@auth/sveltekit\">\n        <img src=\"https://img.shields.io/npm/dm/@auth/sveltekit?label=downloads&style=flat-square\" alt=\"Downloads\" />\n      </a>\n      <a href=\"https://npm.im/@auth/sveltekit\">\n        <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n      </a>\n   </p>\n</p>\n\n## Overview\n\nThis is the official SvelteKit Auth example for [Auth.js](https://sveltekit.authjs.dev).\n\n## Getting started\n\nYou can instantly deploy this example to [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=sveltekit-auth-example) by clicking the following button.\n\n[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/nextauthjs/sveltekit-auth-example&project-name=sveltekit-auth-example&repository-name=sveltekit-auth-example)\n"
  },
  {
    "path": "apps/examples/sveltekit/package.json",
    "content": "{\n  \"private\": true,\n  \"description\": \"An example project for Auth.js with SvelteKit\",\n  \"repository\": \"https://github.com/nextauthjs/sveltekit-auth-example\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"homepage\": \"https://sveltekit-auth-example.vercel.app\",\n  \"scripts\": {\n    \"dev\": \"vite dev\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\",\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    \"@sveltejs/adapter-auto\": \"3.2.0\",\n    \"@sveltejs/kit\": \"2.8.3\",\n    \"@sveltejs/vite-plugin-svelte\": \"^3.1.0\",\n    \"svelte\": \"4.2.19\",\n    \"svelte-check\": \"3.6.9\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"5.2.14\"\n  },\n  \"dependencies\": {\n    \"@auth/sveltekit\": \"latest\"\n  },\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "apps/examples/sveltekit/src/app.d.ts",
    "content": "/// <reference types=\"@auth/sveltekit\" />\n"
  },
  {
    "path": "apps/examples/sveltekit/src/app.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <link rel=\"icon\" href=\"%sveltekit.assets%/favicon.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width\" />\n    %sveltekit.head%\n  </head>\n\n  <body>\n    <div>%sveltekit.body%</div>\n  </body>\n</html>\n"
  },
  {
    "path": "apps/examples/sveltekit/src/auth.ts",
    "content": "import { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Apple from \"@auth/sveltekit/providers/apple\"\nimport Auth0 from \"@auth/sveltekit/providers/auth0\"\nimport AzureB2C from \"@auth/sveltekit/providers/azure-ad-b2c\"\nimport BoxyHQSAML from \"@auth/sveltekit/providers/boxyhq-saml\"\nimport Cognito from \"@auth/sveltekit/providers/cognito\"\nimport Coinbase from \"@auth/sveltekit/providers/coinbase\"\nimport Discord from \"@auth/sveltekit/providers/discord\"\nimport Dropbox from \"@auth/sveltekit/providers/dropbox\"\nimport Facebook from \"@auth/sveltekit/providers/facebook\"\nimport GitHub from \"@auth/sveltekit/providers/github\"\nimport GitLab from \"@auth/sveltekit/providers/gitlab\"\nimport Google from \"@auth/sveltekit/providers/google\"\nimport Hubspot from \"@auth/sveltekit/providers/hubspot\"\nimport Keycloak from \"@auth/sveltekit/providers/keycloak\"\nimport LinkedIn from \"@auth/sveltekit/providers/linkedin\"\nimport Netlify from \"@auth/sveltekit/providers/netlify\"\nimport Okta from \"@auth/sveltekit/providers/okta\"\nimport Passage from \"@auth/sveltekit/providers/passage\"\nimport Pinterest from \"@auth/sveltekit/providers/pinterest\"\nimport Reddit from \"@auth/sveltekit/providers/reddit\"\nimport Slack from \"@auth/sveltekit/providers/slack\"\nimport Spotify from \"@auth/sveltekit/providers/spotify\"\nimport Twitch from \"@auth/sveltekit/providers/twitch\"\nimport Twitter from \"@auth/sveltekit/providers/twitter\"\nimport WorkOS from \"@auth/sveltekit/providers/workos\"\nimport Zoom from \"@auth/sveltekit/providers/zoom\"\nimport { env } from \"$env/dynamic/private\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  trustHost: true,\n  providers: [\n    Apple,\n    Auth0,\n    AzureB2C({\n      clientId: env.AUTH_AZURE_AD_B2C_ID,\n      clientSecret: env.AUTH_AZURE_AD_B2C_SECRET,\n      issuer: env.AUTH_AZURE_AD_B2C_ISSUER,\n    }),\n    BoxyHQSAML({\n      clientId: \"dummy\",\n      clientSecret: \"dummy\",\n      issuer: env.AUTH_BOXYHQ_SAML_ISSUER,\n    }),\n    Cognito,\n    Coinbase,\n    Discord,\n    Dropbox,\n    Facebook,\n    GitHub,\n    GitLab,\n    Google,\n    Hubspot,\n    Keycloak,\n    LinkedIn,\n    Netlify,\n    Okta,\n    Passage,\n    Pinterest,\n    Reddit,\n    Slack,\n    Spotify,\n    Twitch,\n    Twitter,\n    WorkOS({\n      connection: env.AUTH_WORKOS_CONNECTION!,\n    }),\n    Zoom,\n  ],\n})\n"
  },
  {
    "path": "apps/examples/sveltekit/src/components/external-icon.svelte",
    "content": "<svg\n  xmlns=\"http://www.w3.org/2000/svg\"\n  width=\"16\"\n  height=\"16\"\n  viewBox=\"0 0 24 24\"\n  fill=\"none\"\n  stroke=\"currentColor\"\n  stroke-width=\"2\"\n  stroke-linecap=\"round\"\n  stroke-linejoin=\"round\"\n  class=\"inline-block ml-0.5 w-4 h-4\"\n  ><path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"\n  ></path><polyline points=\"15 3 21 3 21 9\"></polyline><line\n    x1=\"10\"\n    x2=\"21\"\n    y1=\"14\"\n    y2=\"3\"\n  ></line></svg\n>\n"
  },
  {
    "path": "apps/examples/sveltekit/src/components/footer.svelte",
    "content": "<script lang=\"ts\">\n  import packageJSON from \"@auth/sveltekit/package.json\"\n  import ExternalIcon from \"./external-icon.svelte\"\n</script>\n\n<footer>\n  <div>\n    <a href=\"https://sveltekit.authjs.dev\">Documentation <ExternalIcon /></a>\n    <a href=\"https://www.npmjs.com/package/next-auth\"> NPM <ExternalIcon /> </a>\n    <a\n      href=\"https://github.com/nextauthjs/next-auth/tree/main/apps/examples/sveltekit\"\n    >\n      Source on GitHub <ExternalIcon />\n    </a>\n    <a class=\"no-underline\" href=\"/policy\">Policy</a>\n  </div>\n  <div>\n    <img src=\"https://authjs.dev/img/logo-sm.png\" alt=\"Auth.js Logo\" />\n    <a href=\"https://www.npmjs.com/package/@auth/sveltekit\">\n      @auth/sveltekit@{packageJSON.version}\n      <ExternalIcon />\n    </a>\n  </div>\n</footer>\n\n<style>\n  footer {\n    display: flex;\n    width: 100%;\n    justify-content: space-between;\n    align-items: center;\n    padding-inline: 1.25rem;\n    margin-block: 2rem;\n  }\n  footer > div {\n    gap: 1rem;\n    display: flex;\n    flex-wrap: wrap;\n    align-items: center;\n  }\n  footer a {\n    color: #333;\n    text-underline-offset: 4px;\n    font-weight: 500;\n    display: flex;\n    align-items: flex-end;\n    gap: 0.25rem;\n  }\n  footer img {\n    width: 24px;\n    height: 24px;\n  }\n  .no-underline {\n    text-decoration: none;\n  }\n</style>\n"
  },
  {
    "path": "apps/examples/sveltekit/src/components/header.svelte",
    "content": "<script lang=\"ts\">\n  import { page } from \"$app/stores\"\n  import { SignIn, SignOut } from \"@auth/sveltekit/components\"\n</script>\n\n<header>\n  <div class=\"signedInStatus\">\n    <div class=\"nojs-show loaded\">\n      <img\n        alt=\"User avatar\"\n        src={$page.data?.session?.user?.image ??\n          `https://api.dicebear.com/9.x/thumbs/svg?seed=${Math.floor(Math.random() * 100000) + 1}&randomizeIds=true`}\n        class=\"avatar\"\n      />\n      {#if $page.data.session}\n        <span class=\"signedInText\">\n          {$page.data.session.user?.email ?? $page.data.session.user?.name}\n        </span>\n        <SignOut>\n          <div slot=\"submitButton\" class=\"buttonPrimary\">Sign out</div>\n        </SignOut>\n      {:else}\n        <span class=\"notSignedInText\">You are not signed in</span>\n        <SignIn>\n          <div slot=\"submitButton\" class=\"buttonPrimary\">Sign in</div>\n        </SignIn>\n      {/if}\n    </div>\n  </div>\n  <nav>\n    <ul class=\"navItems\">\n      <li class=\"navItem\"><a href=\"/\">Home</a></li>\n      <li class=\"navItem\"><a href=\"/protected\">Protected</a></li>\n    </ul>\n  </nav>\n</header>\n\n<style>\n  .nojs-show {\n    opacity: 1;\n    top: 0;\n  }\n  .signedInStatus {\n    display: block;\n    min-height: 4rem;\n  }\n  .loaded {\n    position: relative;\n    top: 0;\n    opacity: 1;\n    overflow: hidden;\n    display: flex;\n    align-items: center;\n    border-radius: 0 0 0.6rem 0.6rem;\n    padding: 0.6rem 1rem;\n    margin: 0;\n    background-color: rgba(0, 0, 0, 0.05);\n    transition: all 0.2s ease-in;\n  }\n  .signedInText,\n  .notSignedInText {\n    justify-content: end;\n    padding-left: 1rem;\n    white-space: nowrap;\n    text-overflow: ellipsis;\n    overflow: hidden;\n    display: inherit;\n    line-height: 1.3rem;\n    flex: 1;\n  }\n  .signedInText {\n    padding-top: 0rem;\n    left: 4.6rem;\n  }\n  .avatar {\n    border-radius: 2rem;\n    float: left;\n    height: 2.8rem;\n    width: 2.8rem;\n    background-color: white;\n    background-size: cover;\n    background-repeat: no-repeat;\n  }\n  .buttonPrimary {\n    font-weight: 500;\n    border-radius: 0.3rem;\n    cursor: pointer;\n    font-size: 1rem;\n    line-height: 1.4rem;\n    position: relative;\n    justify-self: end;\n    background-color: #346df1;\n    color: #fff;\n    text-decoration: none;\n    padding: 0.7rem 1.4rem;\n  }\n  .buttonPrimary:hover {\n    box-shadow: inset 0 0 5rem rgba(0, 0, 0, 0.2);\n  }\n  .navItems {\n    margin-bottom: 2rem;\n    padding: 0;\n    list-style: none;\n  }\n  .navItem {\n    display: inline-block;\n    margin-right: 1rem;\n  }\n  :global(form button) {\n    border: none !important;\n  }\n</style>\n"
  },
  {
    "path": "apps/examples/sveltekit/src/hooks.server.ts",
    "content": "export { handle } from \"./auth\"\n"
  },
  {
    "path": "apps/examples/sveltekit/src/routes/+layout.server.ts",
    "content": "import type { LayoutServerLoad } from \"./$types\"\n\nexport const load: LayoutServerLoad = async (event) => {\n  return {\n    session: await event.locals.auth(),\n  }\n}\n"
  },
  {
    "path": "apps/examples/sveltekit/src/routes/+layout.svelte",
    "content": "<script lang=\"ts\">\n  import Header from \"$components/header.svelte\"\n  import Footer from \"$components/footer.svelte\"\n</script>\n\n<div class=\"container\">\n  <Header />\n  <slot />\n  <Footer />\n</div>\n\n<style>\n  :global(body) {\n    font-family:\n      ui-sans-serif,\n      system-ui,\n      -apple-system,\n      BlinkMacSystemFont,\n      \"Segoe UI\",\n      Roboto,\n      \"Helvetica Neue\",\n      Arial,\n      \"Noto Sans\",\n      sans-serif,\n      \"Apple Color Emoji\",\n      \"Segoe UI Emoji\",\n      \"Segoe UI Symbol\",\n      \"Noto Color Emoji\";\n    padding: 0 1rem 0rem 1rem;\n    max-width: 768px;\n    margin: 0 auto;\n    background: #fff;\n    color: #333;\n  }\n  :global(li),\n  :global(p) {\n    line-height: 1.5rem;\n  }\n  :global(a) {\n    font-weight: 500;\n  }\n  :global(hr) {\n    border: 1px solid #ddd;\n  }\n  :global(iframe) {\n    background: #ccc;\n    border: 1px solid #ccc;\n    height: 10rem;\n    width: 100%;\n    border-radius: 0.5rem;\n    filter: invert(1);\n  }\n  .container {\n    height: 100dvh;\n    display: flex;\n    flex-direction: column;\n    justify-content: start;\n    align-items: stretch;\n  }\n</style>\n"
  },
  {
    "path": "apps/examples/sveltekit/src/routes/+page.svelte",
    "content": "<main>\n  <h1>SvelteKit Auth Example</h1>\n  <p>\n    This is an example site to demonstrate how to use <a\n      href=\"https://kit.svelte.dev/\">SvelteKit</a\n    >\n    with <a href=\"https://sveltekit.authjs.dev\">SvelteKit Auth</a> for authentication.\n  </p>\n</main>\n\n<style>\n  main {\n    flex: 1;\n  }\n</style>\n"
  },
  {
    "path": "apps/examples/sveltekit/src/routes/protected/+page.svelte",
    "content": "<script lang=\"ts\">\n  import { page } from \"$app/stores\"\n</script>\n\n{#if $page.data.session}\n  <h1>Protected page</h1>\n  <p>\n    This is a protected content. You can access this content because you are\n    signed in.\n  </p>\n  <p>Session expiry: {$page.data.session?.expires}</p>\n{:else}\n  <h1>Access Denied</h1>\n{/if}\n"
  },
  {
    "path": "apps/examples/sveltekit/src/routes/signin/+page.server.ts",
    "content": "import { signIn } from \"../../auth\"\nimport type { Actions } from \"./$types\"\n\nexport const actions = { default: signIn } satisfies Actions\n"
  },
  {
    "path": "apps/examples/sveltekit/src/routes/signout/+page.server.ts",
    "content": "import { signOut } from \"../../auth\"\nimport type { Actions } from \"./$types\"\n\nexport const actions = { default: signOut } satisfies Actions\n"
  },
  {
    "path": "apps/examples/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://kit.svelte.dev/docs/integrations#preprocessors\n  // for more information about preprocessors\n  preprocess: vitePreprocess(),\n\n  kit: {\n    adapter: adapter(),\n    alias: {\n      $components: \"src/components\",\n      $lib: \"src/components\",\n      $routes: \"src/routes\",\n    },\n  },\n}\n\nexport default config\n"
  },
  {
    "path": "apps/examples/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  }\n  // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias\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": "apps/examples/sveltekit/vite.config.js",
    "content": "import { sveltekit } from \"@sveltejs/kit/vite\"\nimport { defineConfig } from \"vite\"\n\nexport default defineConfig({\n  server: {\n    port: 3000,\n  },\n  plugins: [sveltekit()],\n})\n"
  },
  {
    "path": "apps/playgrounds/README.md",
    "content": "<p align=\"center\">\n   <br/>\n   <a href=\"https://authjs.dev\" target=\"_blank\"><img width=\"150px\" src=\"https://authjs.dev/img/logo-sm.png\" /></a>\n   <h3 align=\"center\">Auth.js library</a></h3>\n   <h4 align=\"center\">Authentication for the Web.</h4>\n   <p align=\"center\" style=\"align: center;\">\n      <a href=\"https://npm.im/next-auth\">\n        <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n      </a>\n      <a href=\"https://npm.im/@auth/core\">\n        <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/core?color=green&label=@auth/core&style=flat-square\">\n      </a>\n      <a href=\"https://www.npmtrends.com/@auth/core\">\n        <img src=\"https://img.shields.io/npm/dm/@auth/core?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n      </a>\n      <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n        <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n      </a>\n   </p>\n</p>\n\nThe playgrounds have been moved to [nextauthjs/playgrounds](https://github.com/nextauthjs/playgrounds).\n"
  },
  {
    "path": "apps/proxy/.gitignore",
    "content": "# dependencies\n/node_modules\n\n# misc\n.DS_Store\n*.pem\n\n# local env files\n.env*.local\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\n"
  },
  {
    "path": "apps/proxy/README.md",
    "content": "TODO\n"
  },
  {
    "path": "apps/proxy/api/[auth].ts",
    "content": "import { Auth, setEnvDefaults, type AuthConfig } from \"@auth/core\"\nimport Apple from \"@auth/core/providers/apple\"\nimport Auth0 from \"@auth/core/providers/auth0\"\nimport AzureB2C from \"@auth/core/providers/azure-ad-b2c\"\nimport BankId from \"@auth/core/providers/bankid-no\"\nimport BoxyHQSAML from \"@auth/core/providers/boxyhq-saml\"\nimport Cognito from \"@auth/core/providers/cognito\"\nimport Coinbase from \"@auth/core/providers/coinbase\"\nimport Discord from \"@auth/core/providers/discord\"\nimport Dropbox from \"@auth/core/providers/dropbox\"\nimport Facebook from \"@auth/core/providers/facebook\"\nimport GitHub from \"@auth/core/providers/github\"\nimport GitLab from \"@auth/core/providers/gitlab\"\nimport Google from \"@auth/core/providers/google\"\nimport Hubspot from \"@auth/core/providers/hubspot\"\nimport Keycloak from \"@auth/core/providers/keycloak\"\nimport LinkedIn from \"@auth/core/providers/linkedin\"\nimport MicrosoftEntraId from \"@auth/core/providers/microsoft-entra-id\"\nimport Netlify from \"@auth/core/providers/netlify\"\nimport Okta from \"@auth/core/providers/okta\"\nimport Passage from \"@auth/core/providers/passage\"\nimport Pinterest from \"@auth/core/providers/pinterest\"\nimport Reddit from \"@auth/core/providers/reddit\"\nimport Salesforce from \"@auth/core/providers/salesforce\"\nimport Slack from \"@auth/core/providers/slack\"\nimport Spotify from \"@auth/core/providers/spotify\"\nimport Twitch from \"@auth/core/providers/twitch\"\nimport Twitter from \"@auth/core/providers/twitter\"\nimport Vipps from \"@auth/core/providers/vipps\"\nimport WorkOS from \"@auth/core/providers/workos\"\nimport Zoom from \"@auth/core/providers/zoom\"\n\nconst authConfig: AuthConfig = {\n  providers: [\n    Apple,\n    Auth0,\n    AzureB2C,\n    BankId,\n    BoxyHQSAML({\n      clientId: \"dummy\",\n      clientSecret: \"dummy\",\n      issuer: process.env.AUTH_BOXYHQ_SAML_ISSUER,\n    }),\n    Cognito,\n    Coinbase,\n    Discord,\n    Dropbox,\n    Facebook,\n    GitHub,\n    GitLab,\n    Google,\n    Hubspot,\n    Keycloak,\n    LinkedIn,\n    MicrosoftEntraId,\n    Netlify,\n    Okta,\n    Passage,\n    Pinterest,\n    Reddit,\n    Salesforce,\n    Slack,\n    Spotify,\n    Twitch,\n    Twitter,\n    Vipps,\n    WorkOS,\n    Zoom,\n    {\n      id: \"tiktok\",\n      name: \"TikTok\",\n      type: \"oauth\",\n      checks: [\"state\"],\n      clientId: process.env.AUTH_TIKTOK_ID,\n      clientSecret: process.env.AUTH_TIKTOK_SECRET,\n      authorization: {\n        url: \"https://www.tiktok.com/v2/auth/authorize\",\n        params: {\n          client_key: process.env.AUTH_TIKTOK_ID,\n          scope: \"user.info.basic\",\n        },\n      },\n      token: \"https://open.tiktokapis.com/v2/oauth/token/\",\n      userinfo:\n        \"https://open.tiktokapis.com/v2/user/info/?fields=open_id,avatar_url,display_name,username\",\n      profile(profile: any) {\n        return profile\n      },\n      style: {\n        bg: \"#000\",\n        text: \"#fff\",\n      },\n    },\n  ],\n  basePath: \"/api\",\n}\nsetEnvDefaults(process.env, authConfig)\n\nexport default function handler(req: Request) {\n  return Auth(req, authConfig)\n}\n\nexport const config = { runtime: \"edge\" }\n"
  },
  {
    "path": "apps/proxy/api/callback/[auth].ts",
    "content": "export { default } from \"../[auth].js\"\nexport const config = { runtime: \"edge\" }\n"
  },
  {
    "path": "apps/proxy/package.json",
    "content": "{\n  \"name\": \"proxy\",\n  \"description\": \"Proxy for Auth.js hosted examples\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"packageManager\": \"pnpm@9.10.0\",\n  \"dependencies\": {\n    \"@auth/core\": \"latest\"\n  }\n}\n"
  },
  {
    "path": "apps/proxy/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"strict\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"NodeNext\",\n    \"moduleResolution\": \"NodeNext\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"skipLibCheck\": true\n  },\n  \"include\": [\"**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "docs/.gitignore",
    "content": ".next\nnode_modules\n.env*\ntsconfig.tsbuildinfo\n\n# Sitemap - Autogenerated\npublic/sitemap.xml\npublic/robots.txt\n\n# Generated Typedoc\n!pages/reference/_meta.js\n!pages/reference/warnings.mdx\npages/reference/*\n"
  },
  {
    "path": "docs/.vscode/settings.json",
    "content": "{\n  \"cSpell.words\": [\"Passwordless\"]\n}\n"
  },
  {
    "path": "docs/LICENSE",
    "content": "ISC License\n\nCopyright (c) 2022-2024, Balázs Orbán\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n"
  },
  {
    "path": "docs/README.md",
    "content": "<img src=\"public/img/logo-sm.png\" alt=\"Logo\" width=\"118\" height=\"128\" align=\"right\" />\n\n# Auth.js Docs\n\n## Quick Start\n\nFirst, run `pnpm i` to install the dependencies.\n\nThen, run `pnpm dev` to start the development server and visit localhost:3000.\n\n## License\n\nThis project is licensed under the MIT License.\n"
  },
  {
    "path": "docs/app/api/cron/route.ts",
    "content": "import { createClient } from \"@vercel/kv\"\nimport { NextResponse, NextRequest } from \"next/server\"\n\nexport async function GET(req: NextRequest) {\n  // Check Authorization\n  const authHeader = req.headers.get(\"authorization\")\n  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {\n    return new NextResponse(\"Unauthorized\", {\n      status: 401,\n    })\n  }\n\n  // Get Vercel KV client\n  const webAuthnKV = createClient({\n    url: process.env.KV_REST_API_URL!,\n    token: process.env.KV_REST_API_TOKEN!,\n  })\n\n  // Drop all WebAuthn authenticators\n  await webAuthnKV.flushall()\n\n  // Verify\n  const allKeys = await webAuthnKV.keys(\"*\")\n\n  if (!allKeys.length) {\n    return NextResponse.json({ ok: true })\n  } else {\n    return new NextResponse(\"Flush Failed\", {\n      status: 500,\n    })\n  }\n}\n"
  },
  {
    "path": "docs/app/api/og/route.tsx",
    "content": "import { ImageResponse } from \"next/og\"\nimport type { NextRequest } from \"next/server\"\n\nexport const runtime = \"edge\"\n\nconst medium = fetch(new URL(\"./Inter-Light.ttf\", import.meta.url)).then(\n  (res) => res.arrayBuffer()\n)\n\nconst bold = fetch(new URL(\"./Inter-Bold.ttf\", import.meta.url)).then((res) =>\n  res.arrayBuffer()\n)\n\nconst foreground = \"hsl(0 0% 98%)\"\nconst mutedForeground = \"hsl(0 0% 53.9%)\"\nconst background = \"rgba(10, 10, 10, 0.90)\"\n\nexport async function GET(request: NextRequest) {\n  const { searchParams } = new URL(request.url!)\n  const title = searchParams.get(\"title\")\n  const description = searchParams.get(\"description\")\n\n  var url = `data:image/svg+xml;base64,${btoa(backgroundSvg)}`\n\n  return new ImageResponse(\n    OG({\n      title: title ?? \"Authentication for the Web.\",\n      description: description ?? \"\",\n      bgSvg: url,\n    }),\n    {\n      width: 1200,\n      height: 630,\n      fonts: [\n        { name: \"Inter\", data: await medium, weight: 300 },\n        { name: \"Inter\", data: await bold, weight: 800 },\n      ],\n    }\n  )\n}\n\nfunction OG({\n  title,\n  description,\n  bgSvg,\n}: {\n  bgSvg: string\n  title: string\n  description: string\n}) {\n  return (\n    <div\n      tw=\"flex flex-col w-full h-full pb-12 pt-16 px-16\"\n      style={{\n        fontFamily: \"'Inter', system-ui, sans-serif\",\n        color: foreground,\n        backgroundSize: \"1200 630\",\n      }}\n    >\n      <img\n        tw=\"absolute top-0 left-0\"\n        style={{ width: 1200, height: 660 }}\n        src={bgSvg}\n      ></img>\n      <div\n        tw=\"flex flex-col justify-center rounded-2xl p-3 shadow-2xl\"\n        style={{\n          background:\n            \"linear-gradient(to right bottom, rgb(40, 158, 249), rgb(91, 33, 182))\",\n          // New colors: \"linear-gradient(to right bottom, rgb(255, 68, 0), rgb(187, 68, 204))\",\n        }}\n      >\n        <div\n          tw=\"flex flex-col items-center rounded-xl p-12\"\n          style={{\n            border: \"1px rgba(156,163,175,0.3)\",\n            background,\n          }}\n        >\n          <p\n            style={{\n              fontSize: \"3.5rem\",\n              fontWeight: 300,\n              maxHeight: \"14rem\",\n              overflow: \"hidden\",\n              textAlign: \"center\",\n              textWrap: \"balance\",\n            }}\n          >\n            {title}\n          </p>\n        </div>\n      </div>\n\n      <div tw=\"flex flex-row items-center justify-between mt-auto p-4\">\n        <div tw=\"flex\">\n          <img\n            src=\"https://authjs.dev/img/logo-sm.png\"\n            width={77}\n            height={90}\n          />\n          <p\n            style={{\n              fontWeight: 800,\n              marginLeft: \"1rem\",\n              fontSize: \"2.3rem\",\n            }}\n          >\n            Auth.js\n          </p>\n        </div>\n        <p\n          style={{\n            marginLeft: \"3rem\",\n            color: mutedForeground,\n            fontWeight: 300,\n            fontSize: \"2rem\",\n          }}\n        >\n          {description}\n        </p>\n      </div>\n    </div>\n  )\n}\n\nconst backgroundSvg = `\n<svg width=\"2560\" height=\"1280\" viewBox=\"0 0 2560 1280\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<g clip-path=\"url(#clip0_511_47)\">\n<rect width=\"2560\" height=\"1280\" fill=\"#111111\"/>\n<path d=\"M438.668 1160.38C1283.52 617.831 3403.85 1713.01 2144.14 1267.27L1980.48 1809.63C1746.84 1726.27 1275.31 1558.8 1258.29 1555.83C1237 1552.12 -35.3605 1436.43 151.807 1269.12C242.949 1225.11 254.011 1278.96 438.668 1160.38Z\" fill=\"url(#paint0_radial_511_47)\"/>\n<path d=\"M2335.05 977.4C1638.8 708.04 1258.25 143.506 1155 -105.091L2257.61 -196C2573.53 307.367 3031.29 1246.76 2335.05 977.4Z\" fill=\"url(#paint1_radial_511_47)\"/>\n<path d=\"M888.078 42.3028C465.467 1294.94 73.8505 662.761 -266.668 854.807L-294.52 -12.0731C-119.086 -25.2806 234.782 -53.0437 246.789 -58.4363C261.797 -65.1771 1132.31 -620.045 1050.51 -275.273C1001.27 -160.866 980.447 -231.483 888.078 42.3028Z\" fill=\"url(#paint2_radial_511_47)\"/>\n</g>\n<defs>\n<radialGradient id=\"paint0_radial_511_47\" cx=\"0\" cy=\"0\" r=\"1\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"translate(1016.5 939) rotate(93.0267) scale(757.557 879.417)\">\n<stop stop-color=\"#FF7722\"/>\n<stop offset=\"1\" stop-color=\"#111111\"/>\n</radialGradient>\n<radialGradient id=\"paint1_radial_511_47\" cx=\"0\" cy=\"0\" r=\"1\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"translate(1653 619) rotate(-46.5881) scale(548.572 1194.34)\">\n<stop stop-color=\"#BB44CC\"/>\n<stop offset=\"1\" stop-color=\"#111111\"/>\n</radialGradient>\n<radialGradient id=\"paint2_radial_511_47\" cx=\"0\" cy=\"0\" r=\"1\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"translate(666.999 571.116) rotate(-129.513) scale(513.947 862.777)\">\n<stop stop-color=\"rgb(40, 158, 249)\"/>\n<stop offset=\"1\" stop-color=\"#111111\"/>\n</radialGradient>\n<clipPath id=\"clip0_511_47\">\n<rect width=\"2560\" height=\"1280\" fill=\"white\"/>\n</clipPath>\n</defs>\n</svg>\n`\n\n// New colors - '#44BBCC', '#BB44CC', '#FF4400'\n"
  },
  {
    "path": "docs/components/Accordion/index.tsx",
    "content": "\"use client\"\n\n// From Fumadocs: https://github.com/fuma-nama/fumadocs/blob/dev/packages/ui/src/components/accordion.tsx\n\nimport * as AccordionPrimitive from \"@radix-ui/react-accordion\"\nimport type {\n  AccordionMultipleProps,\n  AccordionSingleProps,\n} from \"@radix-ui/react-accordion\"\nimport { Check, Link, CaretRight } from \"@/icons\"\nimport {\n  forwardRef,\n  type ComponentPropsWithoutRef,\n  useState,\n  useEffect,\n} from \"react\"\nimport { useCopyButton } from \"@/utils/useCopyButton\"\nimport cx from \"classnames\"\n\nexport const Accordions = forwardRef<\n  HTMLDivElement,\n  AccordionSingleProps | AccordionMultipleProps\n>((props, ref) => {\n  if (props.type === \"multiple\") {\n    return <MultipleAccordions className=\"bg-red-500\" ref={ref} {...props} />\n  }\n\n  return (\n    <SingleAccordions\n      ref={ref}\n      {...props}\n      // If type is undefined\n      type=\"single\"\n    />\n  )\n})\n\nAccordions.displayName = \"Accordions\"\n\nexport const MultipleAccordions = forwardRef<\n  HTMLDivElement,\n  AccordionMultipleProps\n>(({ className, defaultValue, ...props }, ref) => {\n  const [defValue, setDefValue] = useState(defaultValue)\n  const value = props.value ?? defValue\n  const setValue = props.onValueChange?.bind(props) ?? setDefValue\n\n  useEffect(() => {\n    if (window.location.hash.length > 0)\n      setValue([window.location.hash.substring(1)])\n  }, [setValue])\n\n  return (\n    <AccordionPrimitive.Root\n      ref={ref}\n      value={value}\n      onValueChange={setValue}\n      className={className}\n      {...props}\n    />\n  )\n})\n\nMultipleAccordions.displayName = \"MultipleAccordions\"\n\nexport const SingleAccordions = forwardRef<\n  HTMLDivElement,\n  AccordionSingleProps\n>(({ className, defaultValue, ...props }, ref) => {\n  const [defValue, setDefValue] = useState(defaultValue)\n  const value = props.value ?? defValue\n  const setValue = props.onValueChange?.bind(props) ?? setDefValue\n\n  useEffect(() => {\n    if (window.location.hash.length > 0)\n      setValue(window.location.hash.substring(1))\n  }, [setValue])\n\n  return (\n    <AccordionPrimitive.Root\n      ref={ref}\n      value={value}\n      onValueChange={setValue}\n      collapsible\n      className={cx(\n        className,\n        \"mt-4 rounded-lg border border-neutral-200 dark:border-neutral-800\"\n      )}\n      {...props}\n    />\n  )\n})\n\nSingleAccordions.displayName = \"SingleAccordions\"\n\nexport const Accordion = forwardRef<\n  HTMLDivElement,\n  Omit<ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>, \"value\"> & {\n    title: string\n  }\n>(({ title, className, children, ...props }, ref) => {\n  return (\n    <AccordionPrimitive.Item\n      ref={ref}\n      // Use `id` instead if presents\n      value={props.id ?? title}\n      className={cx(\n        \"group/accordion scroll-m-20 border-b border-neutral-200 first:overflow-hidden first:rounded-t-lg last:overflow-hidden last:rounded-b-lg last-of-type:border-b-0 dark:border-neutral-800\",\n        className\n      )}\n      {...props}\n    >\n      <>\n        <AccordionPrimitive.Trigger className=\"focus-visible:ring-ring flex w-full items-center gap-1 px-2 py-[1.125rem] text-left transition-colors duration-300 focus-visible:outline-none focus-visible:ring-2 data-[state=open]:bg-neutral-100 data-[state=open]:dark:bg-neutral-950\">\n          <CaretRight className=\"size-4 transition-transform duration-200 group-data-[state=open]/accordion:rotate-90\" />\n          <span className=\"text-medium text-foreground font-medium\">\n            {title}\n          </span>\n        </AccordionPrimitive.Trigger>\n        {props.id ? <CopyButton id={props.id} /> : null}\n      </>\n      <AccordionPrimitive.Content className=\"data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden\">\n        <div className=\"prose-no-margin py-4 pl-6 text-sm\">{children}</div>\n      </AccordionPrimitive.Content>\n    </AccordionPrimitive.Item>\n  )\n})\n\nfunction CopyButton({ id }: { id: string }): JSX.Element {\n  const [checked, onClick] = useCopyButton(() => {\n    const url = new URL(window.location.href)\n    url.hash = id\n\n    void navigator.clipboard.writeText(url.toString())\n  })\n\n  return (\n    <button\n      type=\"button\"\n      aria-label=\"Copy Link\"\n      className=\"hover:bg-accent hover:text-accent-foreground opacity-0 transition-all group-data-[state=open]/accordion:opacity-100\"\n      onClick={onClick}\n    >\n      {checked ? <Check className=\"size-3.5\" /> : <Link className=\"size-3.5\" />}\n    </button>\n  )\n}\n\nAccordion.displayName = \"Accordion\"\n"
  },
  {
    "path": "docs/components/Blur/index.tsx",
    "content": "function Blur() {\n  return (\n    <div className=\"pointer-events-none absolute right-4 top-[15vh] -z-10 h-96 w-full md:right-24\">\n      <div className=\"animate-blob absolute right-80 top-0 h-96 max-h-[800px] w-96 max-w-[900px] rounded-full bg-violet-200 opacity-60 mix-blend-multiply blur-3xl lg:h-[60vh] lg:w-[40vw] 2xl:right-96 dark:opacity-5 dark:mix-blend-overlay\"></div>\n      <div className=\"animation-delay-2000 animate-blob absolute -top-80 right-0 h-96 max-h-[800px] w-96 max-w-[900px] rounded-full bg-yellow-200 opacity-50 mix-blend-multiply blur-3xl lg:h-[60vh] lg:w-[40vw] 2xl:-right-64 2xl:-top-96 dark:opacity-5 dark:mix-blend-overlay\"></div>\n      <div className=\"animation-delay-4000 animate-blob absolute bottom-0 right-0 h-96 max-h-[800px] w-96 max-w-[900px] rounded-full bg-pink-200 opacity-50 mix-blend-multiply blur-3xl lg:-bottom-80 lg:h-[60vh] lg:w-[40vw] 2xl:-bottom-96 2xl:-right-64 dark:opacity-5 dark:mix-blend-overlay\"></div>\n    </div>\n  )\n}\n\nexport { Blur }\n"
  },
  {
    "path": "docs/components/Code/index.tsx",
    "content": "import { useSearchParams } from \"next/navigation\"\nimport { useRouter } from \"next/router\"\nimport { useThemeConfig } from \"nextra-theme-docs\"\nimport { Tabs } from \"nextra/components\"\nimport React, { Children, useEffect, MouseEvent } from \"react\"\n\ninterface ChildrenProps {\n  children: React.ReactNode\n}\n\nconst AUTHJS_TAB_KEY = \"authjs.codeTab.framework\"\nconst AUTHJS_TAB_KEY_ALL = \"authjs.codeTab.framework.all\"\n\nCode.Next = NextCode\nCode.NextClient = NextClientCode\nCode.Svelte = SvelteCode\n// Code.Solid = SolidCode;\nCode.Express = ExpressCode\nCode.Qwik = QwikCode\n\nconst baseFrameworks = {\n  [NextCode.name]: \"Next.js\",\n  [QwikCode.name]: \"Qwik\",\n  [SvelteCode.name]: \"SvelteKit\",\n  [ExpressCode.name]: \"Express\",\n  // [SolidCode.name]: \"SolidStart\",\n}\n\nconst allFrameworks = {\n  [NextCode.name]: \"Next.js\",\n  [NextClientCode.name]: \"Next.js (Client)\",\n  [QwikCode.name]: \"Qwik\",\n  [SvelteCode.name]: \"SvelteKit\",\n  // [SolidCode.name]: \"SolidStart\",\n  [ExpressCode.name]: \"Express\",\n}\n\nconst findFrameworkKey = (\n  text: string,\n  frameworks: Record<string, string>\n): string | null => {\n  const entry = Object.entries(frameworks).find(([_, value]) => value === text)\n  return entry ? entry[0] : null\n}\n\nconst getIndexFrameworkFromUrl = (\n  url: string,\n  frameworks: Record<string, string>\n): number | null => {\n  const params = new URLSearchParams(url)\n  const paramValue = params.get(\"framework\")\n  if (!paramValue) return null\n\n  const decodedValue = decodeURI(paramValue)\n\n  const index = Object.values(frameworks).findIndex(\n    (value) => value === decodedValue\n  )\n  return index === -1 ? null : index\n}\n\nconst getIndexFrameworkFromStorage = (\n  frameworks: Record<string, string>,\n  isAllFrameworks: boolean\n): number | null => {\n  const storageKey = isAllFrameworks ? AUTHJS_TAB_KEY_ALL : AUTHJS_TAB_KEY\n  const storedIndex = window.localStorage.getItem(storageKey)\n\n  if (!storedIndex) {\n    return null\n  }\n\n  return parseInt(storedIndex) % Object.keys(frameworks).length\n}\n\nconst updateFrameworkStorage = (\n  frameworkURI: string,\n  frameworks: Record<string, string>,\n  isAllFrameworks: boolean\n): void => {\n  const index = Object.values(frameworks).findIndex(\n    (value) => encodeURI(value) === frameworkURI\n  )\n  if (index === -1) return\n\n  const storageKey = isAllFrameworks ? AUTHJS_TAB_KEY_ALL : AUTHJS_TAB_KEY\n  window.localStorage.setItem(storageKey, index.toString())\n\n  // Update other storage if framework exists in other object\n  const otherFrameworksValues = Object.values(\n    isAllFrameworks ? baseFrameworks : allFrameworks\n  )\n  const otherStorageKey = isAllFrameworks ? AUTHJS_TAB_KEY : AUTHJS_TAB_KEY_ALL\n\n  const encodedFrameworksValues = otherFrameworksValues.map((value) =>\n    encodeURI(value)\n  )\n  const existsInOther = encodedFrameworksValues.some(\n    (encodedFramework) => encodedFramework === frameworkURI\n  )\n  if (existsInOther) {\n    const otherIndex = otherFrameworksValues.findIndex(\n      (encodedFramework) => encodedFramework === frameworkURI\n    )\n    window.localStorage.setItem(otherStorageKey, otherIndex.toString())\n    // see https://github.com/shuding/nextra/blob/7ae958f02922e608151411042f658480b75164a6/packages/nextra/src/client/components/tabs/index.client.tsx#L106\n    window.dispatchEvent(\n      new StorageEvent(\"storage\", {\n        key: otherStorageKey,\n        newValue: otherIndex.toString(),\n      })\n    )\n  }\n}\n\nexport function Code({ children }: ChildrenProps) {\n  const router = useRouter()\n  const searchParams = useSearchParams()\n  const childElements = Children.toArray(children)\n  const { project } = useThemeConfig()\n\n  const withNextJsPages = childElements.some(\n    // @ts-expect-error: Hacky dynamic child wrangling\n    (p) => p && p.type.name === NextClientCode.name\n  )\n\n  const renderedFrameworks = withNextJsPages ? allFrameworks : baseFrameworks\n\n  const updateFrameworkInUrl = (frameworkURI: string): void => {\n    if (frameworkURI === \"undefined\") return\n\n    const params = new URLSearchParams(searchParams?.toString())\n    params.set(\"framework\", frameworkURI)\n\n    router.push(`${router.pathname}?${params.toString()}`, undefined, {\n      scroll: false,\n    })\n  }\n\n  const handleClickFramework = (event: MouseEvent<HTMLDivElement>) => {\n    if (!(event.target instanceof HTMLButtonElement)) return\n    const { textContent } = event.target as unknown as HTMLDivElement\n    if (!textContent) return\n\n    const frameworkURI = encodeURI(textContent)\n    updateFrameworkInUrl(frameworkURI)\n    updateFrameworkStorage(frameworkURI, renderedFrameworks, withNextJsPages)\n\n    // Focus and scroll to maintain position when code blocks above are expanded\n    const element = event.target as HTMLButtonElement\n    const rect = element.getBoundingClientRect()\n    requestAnimationFrame(() => {\n      element.focus()\n      window.scrollBy(0, element.getBoundingClientRect().top - rect.top)\n    })\n  }\n\n  useEffect(() => {\n    const indexFrameworkFromStorage = getIndexFrameworkFromStorage(\n      renderedFrameworks,\n      withNextJsPages\n    )\n    const indexFrameworkFromUrl = getIndexFrameworkFromUrl(\n      router.asPath,\n      renderedFrameworks\n    )\n\n    if (indexFrameworkFromStorage === null) {\n      updateFrameworkStorage(\n        encodeURI(renderedFrameworks[indexFrameworkFromUrl ?? 0]),\n        renderedFrameworks,\n        withNextJsPages\n      )\n    }\n\n    if (!indexFrameworkFromUrl) {\n      const index = indexFrameworkFromStorage ?? 0\n      updateFrameworkInUrl(encodeURI(renderedFrameworks[index]))\n    }\n  }, [router.pathname, renderedFrameworks, withNextJsPages])\n\n  return (\n    <div\n      className=\"[&_div[role='tablist']]:!pb-0\"\n      onClick={handleClickFramework}\n    >\n      <Tabs\n        storageKey={withNextJsPages ? AUTHJS_TAB_KEY_ALL : AUTHJS_TAB_KEY}\n        items={Object.values(renderedFrameworks)}\n      >\n        {Object.keys(renderedFrameworks).map((f) => {\n          // @ts-expect-error: Hacky dynamic child wrangling\n          const child = childElements.find((c) => c?.type?.name === f)\n\n          // @ts-expect-error: Hacky dynamic child wrangling\n          return Object.keys(child?.props ?? {}).length ? (\n            child\n          ) : (\n            <Tabs.Tab key={f}>\n              <p className=\"rounded-lg bg-slate-100 p-6 font-semibold dark:bg-neutral-950\">\n                {renderedFrameworks[f]} not documented yet. Help us by\n                contributing{\" \"}\n                <a\n                  className=\"underline\"\n                  target=\"_blank\"\n                  href={`${project.link}/edit/main/docs/pages${router.pathname}.mdx`}\n                  rel=\"noreferrer\"\n                >\n                  here\n                </a>\n                .\n              </p>\n            </Tabs.Tab>\n          )\n        })}\n      </Tabs>\n    </div>\n  )\n}\n\nfunction NextClientCode({ children }: ChildrenProps) {\n  return <Tabs.Tab>{children}</Tabs.Tab>\n}\n\nfunction NextCode({ children }: ChildrenProps) {\n  return <Tabs.Tab>{children}</Tabs.Tab>\n}\n\nfunction SvelteCode({ children }: ChildrenProps) {\n  return <Tabs.Tab>{children}</Tabs.Tab>\n}\n\n// function SolidCode({ children }: ChildrenProps) {\n//   return <Tabs.Tab>{children}</Tabs.Tab>;\n// }\n\nfunction ExpressCode({ children }: ChildrenProps) {\n  return <Tabs.Tab>{children}</Tabs.Tab>\n}\n\nfunction QwikCode({ children }: ChildrenProps) {\n  return <Tabs.Tab>{children}</Tabs.Tab>\n}\n"
  },
  {
    "path": "docs/components/DocSearch/index.tsx",
    "content": "import dynamic from \"next/dynamic\"\n\nconst DocSearch = dynamic(\n  () => import(\"./wrapper\").then((mod) => mod.default),\n  {\n    ssr: false,\n  }\n)\n\nexport default DocSearch\n"
  },
  {
    "path": "docs/components/DocSearch/wrapper.tsx",
    "content": "import { DocSearch } from \"@docsearch/react\"\nimport { useTheme } from \"nextra-theme-docs\"\nimport { useEffect } from \"react\"\n\nimport \"@docsearch/css\"\n\nfunction App() {\n  const { resolvedTheme } = useTheme()\n\n  useEffect(() => {\n    if (resolvedTheme) {\n      // hack to get DocSearch to use dark mode colors if applicable\n      document.documentElement.setAttribute(\"data-theme\", resolvedTheme)\n    }\n  }, [resolvedTheme])\n\n  return (\n    <DocSearch\n      appId={process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!}\n      indexName=\"next-auth\"\n      apiKey={process.env.NEXT_PUBLIC_ALGOLIA_KEY!}\n    />\n  )\n}\n\nexport default App\n"
  },
  {
    "path": "docs/components/Footer/index.tsx",
    "content": "import { useEffect } from \"react\"\nimport cx from \"classnames\"\n\nfunction kFormatter(num: number) {\n  return (Math.sign(num) * (Math.abs(num) / 1000)).toFixed(1) + \"k\"\n}\n\nexport function Footer({ className = \"\" }) {\n  useEffect(() => {\n    fetch(\"https://api.github.com/repos/nextauthjs/next-auth\")\n      .then((res) => res.json())\n      .then((data) => {\n        const githubStat = document.querySelector(\".github-counter\")!\n        if (!githubStat) return\n        githubStat.innerHTML = kFormatter(data.stargazers_count ?? 21100)\n      })\n  }, [])\n  return (\n    <div\n      className={cx(\n        \"mx-auto flex w-full flex-col items-center gap-4 px-12 pb-20 pt-24 text-gray-600 sm:gap-12 dark:text-gray-100\",\n        className\n      )}\n    >\n      <div className=\"flex w-full max-w-[90rem] flex-col justify-between gap-6 sm:flex-row sm:gap-0\">\n        <div className=\"flex flex-col\">\n          <h3 className=\"mb-4 text-lg font-black\">About Auth.js</h3>\n          <ul className=\"flex flex-col gap-2\">\n            <li>\n              <a href=\"/getting-started\">Introduction</a>\n            </li>\n            <li>\n              <a href=\"/security\">Security</a>\n            </li>\n            <li>\n              <a\n                href=\"https://discord.authjs.dev/?utm_source=docs\"\n                title=\"Join our Discord\"\n                rel=\"noopener noreferrer\"\n                className=\"flex items-center gap-1\"\n                target=\"_blank\"\n              >\n                Discord Community\n              </a>\n            </li>\n          </ul>\n        </div>\n        <div className=\"flex flex-col\">\n          <h3 className=\"mb-4 text-lg font-black\">Download</h3>\n          <ul className=\"flex flex-col gap-2\">\n            <a\n              rel=\"noopener noreferrer\"\n              target=\"_blank\"\n              href=\"https://github.com/nextauthjs/next-auth\"\n            >\n              GitHub\n            </a>\n            <a\n              rel=\"noopener noreferrer\"\n              target=\"_blank\"\n              href=\"https://www.npmjs.com/package/next-auth\"\n            >\n              NPM\n            </a>\n          </ul>\n        </div>\n        <div className=\"flex flex-col\">\n          <h3 className=\"mb-4 text-lg font-black\">Acknowledgements</h3>\n          <ul className=\"flex flex-col gap-2\">\n            <a href=\"/contributors\">Contributors</a>\n          </ul>\n        </div>\n      </div>\n      <div className=\"mx-auto mt-4 flex-grow text-gray-400 sm:mt-0 dark:text-gray-500\">\n        Auth.js &copy; Better Auth Inc. - {new Date().getFullYear()}\n      </div>\n    </div>\n  )\n}\n\nexport default Footer\n"
  },
  {
    "path": "docs/components/FrameworkLink/index.tsx",
    "content": "import { Link } from \"@/components/Link\"\nimport { Flask } from \"@/icons/Flask\"\nimport { ArrowSquareOut } from \"@/icons/ArrowSquareOut\"\nimport { GithubLogo } from \"@/icons/GithubLogo\"\n\ninterface FrameworkLinkProps {\n  id: string\n  name: string\n  demo: string\n  repo: string\n  isExperimental?: boolean\n  isInvert?: boolean\n}\n\nexport function FrameworkLink({\n  id,\n  name,\n  demo,\n  repo,\n  isExperimental = true,\n  isInvert = false,\n}: FrameworkLinkProps) {\n  return (\n    <div className=\"group flex flex-col gap-2\">\n      <Link\n        href={`/getting-started/installation?framework=${name}`}\n        className=\"relative flex h-28 w-28 flex-col flex-wrap items-center justify-between rounded-lg border border-solid border-neutral-200 bg-white p-4 !no-underline shadow-sm transition-colors duration-300 hover:bg-neutral-100 hover:bg-neutral-50 dark:border-neutral-800 dark:bg-neutral-900 dark:hover:bg-neutral-950\"\n      >\n        <img\n          alt={`${name} Logo`}\n          src={`/img/etc/${id}.svg`}\n          width=\"40\"\n          className={isInvert ? \"dark:invert\" : \"\"}\n        />\n        <div className=\"mt-3 text-sm\">{name}</div>\n        {isExperimental && (\n          <div\n            className=\"absolute z-10 rounded-full bg-amber-300 p-2 text-sm font-semibold text-black shadow-sm\"\n            style={{ right: \"-20px\", top: \"-15px\" }}\n          >\n            <Flask className=\"size-6\" />\n          </div>\n        )}\n      </Link>\n      <div className=\"flex justify-stretch gap-2\">\n        <a\n          title=\"Live Example\"\n          href={demo}\n          rel=\"noreferrer\"\n          target=\"_blank\"\n          className=\"flex w-full justify-center rounded-md border border-neutral-200/50 bg-neutral-100 p-2 text-sm dark:border-neutral-800/30 dark:bg-neutral-900\"\n        >\n          <ArrowSquareOut className=\"size-5\" />\n        </a>\n        <a\n          title=\"GitHub Repository\"\n          href={repo}\n          rel=\"noreferrer\"\n          target=\"_blank\"\n          className=\"flex w-full justify-center rounded-md border border-neutral-200/50 bg-neutral-100 p-2 text-sm dark:border-neutral-800/30 dark:bg-neutral-900\"\n        >\n          <GithubLogo className=\"size-5\" />\n        </a>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "docs/components/Guides/index.tsx",
    "content": "import {\n  ShieldStar,\n  CaretRight,\n  Link as LinkIcon,\n  ArrowRight,\n  Browser,\n  GithubLogo,\n} from \"@/icons\"\nimport Link from \"next/link\"\nimport Image from \"next/image\"\n\nexport function Guides() {\n  return (\n    <section className=\"flex items-center justify-center overflow-hidden bg-neutral-100 pb-12 pt-24 dark:bg-neutral-950\">\n      <div className=\"flex w-full max-w-5xl flex-col items-center justify-between gap-10 lg:flex-row lg:items-start\">\n        <div className=\"flex w-full max-w-2xl flex-1 flex-col items-start justify-start px-8 lg:px-0\">\n          <div className=\"mb-10 flex w-full items-center justify-between\">\n            <h2 className=\"text-2xl lg:text-3xl\">Highlighted Guides</h2>\n            <Link\n              href=\"/guides/configuring-oauth-providers\"\n              className=\"flex items-center gap-2 text-[#289ef9]\"\n            >\n              See all\n              <ArrowRight className=\"size-3.5\" />\n            </Link>\n          </div>\n          <ul className=\"w-full list-none\">\n            <Link href=\"/guides/configuring-oauth-providers\">\n              <li className=\"group mb-8 flex w-full justify-between\">\n                <div className=\"flex gap-2\">\n                  <ShieldStar className=\"size-8\" />\n                  <div className=\"flex flex-col items-start\">\n                    Configuring OAuth providers\n                    <span className=\"text-neutral-400 dark:text-neutral-700\">\n                      Customize a built-in one or set up your own.\n                    </span>\n                  </div>\n                </div>\n                <div className=\"opacity-0 transition duration-300 group-hover:opacity-100\">\n                  <CaretRight className=\"size-6\" />\n                </div>\n              </li>\n            </Link>\n            <Link href=\"/guides/configuring-github\">\n              <li className=\"group mb-8 flex w-full justify-between\">\n                <div className=\"flex gap-2\">\n                  <GithubLogo className=\"size-8\" />\n                  <div className=\"flex flex-col items-start\">\n                    OAuth with GitHub\n                    <span className=\"text-neutral-400 dark:text-neutral-700\">\n                      Step-by-step guide to set up an OAuth provider.\n                    </span>\n                  </div>\n                </div>\n                <div className=\"opacity-0 transition duration-300 group-hover:opacity-100\">\n                  <CaretRight className=\"size-6\" />\n                </div>\n              </li>\n            </Link>\n            <Link href=\"/guides/pages/signin\">\n              <li className=\"group mb-8 flex w-full justify-between\">\n                <div className=\"flex gap-2\">\n                  <Browser className=\"size-8\" />\n                  <div className=\"flex flex-col items-start\">\n                    Custom Signin Page\n                    <span className=\"text-neutral-400 dark:text-neutral-700\">\n                      Create a page that matches your app's design.\n                    </span>\n                  </div>\n                </div>\n                <div className=\"opacity-0 transition duration-300 group-hover:opacity-100\">\n                  <CaretRight className=\"size-6\" />\n                </div>\n              </li>\n            </Link>\n          </ul>\n        </div>\n        <div className=\"flex w-full max-w-2xl flex-1 flex-col items-start justify-start px-8 lg:mx-0\">\n          <div className=\"mb-10 flex w-full items-center justify-between\">\n            <h2 className=\"text-2xl lg:text-3xl\">Example Apps</h2>\n          </div>\n          <ul className=\"w-full list-none\">\n            {[\n              {\n                id: \"nextjs\",\n                name: \"Next.js\",\n                demo: \"https://next-auth-example.vercel.app\",\n                repo: \"next-auth-example\",\n              },\n              {\n                id: \"sveltekit\",\n                name: \"SvelteKit\",\n                demo: \"https://sveltekit-auth-example.vercel.app\",\n                repo: \"sveltekit-auth-example\",\n              },\n              {\n                id: \"express\",\n                name: \"Express\",\n                demo: \"https://express-auth-example.vercel.app\",\n                repo: \"express-auth-example\",\n              },\n              {\n                id: \"qwik\",\n                name: \"Qwik\",\n                demo: \"https://qwik-auth-example.vercel.app\",\n                repo: \"qwik-auth-example\",\n              },\n            ].map((f) => (\n              <li\n                key={f.id}\n                className=\"mb-8 flex w-full flex-col justify-between gap-4 p-2 grayscale transition duration-300 hover:grayscale-0 sm:flex-row\"\n              >\n                <div className=\"flex items-center gap-2\">\n                  <Image\n                    src={`/img/etc/${f.id}.svg`}\n                    className={\n                      f.id === \"express\" || f.id === \"nextjs\"\n                        ? \"dark:invert\"\n                        : \"\"\n                    }\n                    height=\"32\"\n                    width=\"32\"\n                    alt={`${f.name} Logo`}\n                  />\n                  {f.name}\n                </div>\n                <div className=\"grid grid-cols-2 gap-4\">\n                  <Link\n                    className=\"flex items-center justify-center gap-2 rounded-md bg-neutral-200 p-3 px-5 outline-none transition duration-300 hover:bg-neutral-300 focus-visible:ring-2 dark:bg-neutral-800 hover:dark:bg-neutral-700\"\n                    target=\"_blank\"\n                    href={f.demo}\n                    rel=\"noreferrer\"\n                  >\n                    <LinkIcon className=\"size-5\" />\n                    Visit\n                  </Link>\n                  <Link\n                    target=\"_blank\"\n                    href={`https://github.com/nextauthjs/${f.repo}`}\n                    className=\"flex items-center justify-center gap-2 rounded-md bg-neutral-200 p-3 px-5 outline-none transition duration-300 hover:bg-neutral-300 focus-visible:ring-2 dark:bg-neutral-800 hover:dark:bg-neutral-700\"\n                    rel=\"noreferrer\"\n                  >\n                    <GithubLogo className=\"size-5\" />\n                    Clone\n                  </Link>\n                </div>\n              </li>\n            ))}\n          </ul>\n        </div>\n      </div>\n    </section>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/ArrowRight.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function ArrowRight({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <line\n        x1=\"40\"\n        y1=\"128\"\n        x2=\"216\"\n        y2=\"128\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <polyline\n        points=\"144 56 216 128 144 200\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/ArrowSquareOut.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function ArrowSquareOut({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <polyline\n        points=\"216 104 215.99 40.01 152 40\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"136\"\n        y1=\"120\"\n        x2=\"216\"\n        y2=\"40\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <path\n        d=\"M184,136v72a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V80a8,8,0,0,1,8-8h72\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/Browser.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function Browser({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <rect\n        x=\"32\"\n        y=\"48\"\n        width=\"192\"\n        height=\"160\"\n        rx=\"8\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"32\"\n        y1=\"96\"\n        x2=\"224\"\n        y2=\"96\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/CaretRight.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function CaretRight({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <polyline\n        points=\"96 48 176 128 96 208\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/ChatCircleText.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function ChatCircleText({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <line\n        x1=\"96\"\n        y1=\"112\"\n        x2=\"160\"\n        y2=\"112\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"96\"\n        y1=\"144\"\n        x2=\"160\"\n        y2=\"144\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <path\n        d=\"M79.93,211.11a96,96,0,1,0-35-35h0L32.42,213.46a8,8,0,0,0,10.12,10.12l37.39-12.47Z\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/Check.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function Check({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <polyline\n        points=\"40 144 96 200 224 72\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/Flask.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function Flask({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <line\n        x1=\"88\"\n        y1=\"32\"\n        x2=\"168\"\n        y2=\"32\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <path\n        d=\"M152,32V99.14l62.85,104.74A8,8,0,0,1,208,216H48a8,8,0,0,1-6.86-12.12L104,99.14V32\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <path\n        d=\"M71.63,153.08c13.23-2.48,32-1.41,56.37,10.92,32.25,16.33,54.75,12.91,67.5,7.65\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/GitBranch.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function GitBranch({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <path\n        d=\"M80,168V144a16,16,0,0,1,16-16h88a16,16,0,0,0,16-16V88\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"80\"\n        y1=\"88\"\n        x2=\"80\"\n        y2=\"168\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <circle\n        cx=\"80\"\n        cy=\"64\"\n        r=\"24\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <circle\n        cx=\"200\"\n        cy=\"64\"\n        r=\"24\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <circle\n        cx=\"80\"\n        cy=\"192\"\n        r=\"24\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/GithubLogo.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function GithubLogo({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <path\n        d=\"M119.83,56A52,52,0,0,0,76,32a51.92,51.92,0,0,0-3.49,44.7A49.28,49.28,0,0,0,64,104v8a48,48,0,0,0,48,48h48a48,48,0,0,0,48-48v-8a49.28,49.28,0,0,0-8.51-27.3A51.92,51.92,0,0,0,196,32a52,52,0,0,0-43.83,24Z\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <path\n        d=\"M104,232V192a32,32,0,0,1,32-32h0a32,32,0,0,1,32,32v40\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <path\n        d=\"M104,208H72a32,32,0,0,1-32-32A32,32,0,0,0,8,144\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/Link.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function Link({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <path\n        d=\"M141.38,64.68l11-11a46.62,46.62,0,0,1,65.94,0h0a46.62,46.62,0,0,1,0,65.94L193.94,144,183.6,154.34a46.63,46.63,0,0,1-66-.05h0A46.48,46.48,0,0,1,104,120.06\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <path\n        d=\"M114.62,191.32l-11,11a46.63,46.63,0,0,1-66-.05h0a46.63,46.63,0,0,1,.06-65.89L72.4,101.66a46.62,46.62,0,0,1,65.94,0h0A46.45,46.45,0,0,1,152,135.94\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/Plus.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function Plus({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <line\n        x1=\"40\"\n        y1=\"128\"\n        x2=\"216\"\n        y2=\"128\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"128\"\n        y1=\"40\"\n        x2=\"128\"\n        y2=\"216\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/SealWarning.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function SealWarning({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <path\n        d=\"M54.46,201.54c-9.2-9.2-3.1-28.53-7.78-39.85C41.82,150,24,140.5,24,128s17.82-22,22.68-33.69C51.36,83,45.26,63.66,54.46,54.46S83,51.36,94.31,46.68C106.05,41.82,115.5,24,128,24S150,41.82,161.69,46.68c11.32,4.68,30.65-1.42,39.85,7.78s3.1,28.53,7.78,39.85C214.18,106.05,232,115.5,232,128S214.18,150,209.32,161.69c-4.68,11.32,1.42,30.65-7.78,39.85s-28.53,3.1-39.85,7.78C150,214.18,140.5,232,128,232s-22-17.82-33.69-22.68C83,204.64,63.66,210.74,54.46,201.54Z\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"128\"\n        y1=\"80\"\n        x2=\"128\"\n        y2=\"136\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <circle cx=\"128\" cy=\"172\" r=\"12\" />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/ShieldStar.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function ShieldStar({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <line\n        x1=\"128\"\n        y1=\"96\"\n        x2=\"128\"\n        y2=\"136\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"88\"\n        y1=\"120\"\n        x2=\"128\"\n        y2=\"136\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"104\"\n        y1=\"168\"\n        x2=\"128\"\n        y2=\"136\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"152\"\n        y1=\"168\"\n        x2=\"128\"\n        y2=\"136\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"168\"\n        y1=\"120\"\n        x2=\"128\"\n        y2=\"136\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <path\n        d=\"M216,112V56a8,8,0,0,0-8-8H48a8,8,0,0,0-8,8v56c0,96,88,120,88,120S216,208,216,112Z\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/Sparkle.tsx",
    "content": "type Props = {\n  className?: string\n}\n\nexport function Sparkle({ className }: Props) {\n  return (\n    <svg\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 256 256\"\n    >\n      <rect width=\"256\" height=\"256\" fill=\"none\" />\n      <path\n        d=\"M84.27,171.73l-55.09-20.3a7.92,7.92,0,0,1,0-14.86l55.09-20.3,20.3-55.09a7.92,7.92,0,0,1,14.86,0l20.3,55.09,55.09,20.3a7.92,7.92,0,0,1,0,14.86l-55.09,20.3-20.3,55.09a7.92,7.92,0,0,1-14.86,0Z\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"176\"\n        y1=\"16\"\n        x2=\"176\"\n        y2=\"64\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"224\"\n        y1=\"72\"\n        x2=\"224\"\n        y2=\"104\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"152\"\n        y1=\"40\"\n        x2=\"200\"\n        y2=\"40\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n      <line\n        x1=\"208\"\n        y1=\"88\"\n        x2=\"240\"\n        y2=\"88\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"16\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/Icons/index.tsx",
    "content": "// Downloaded and copied from: https://phosphoricons.com/\n\nexport { Check } from \"./Check\"\nexport { Link } from \"./Link\"\nexport { Plus } from \"./Plus\"\nexport { ArrowSquareOut } from \"./ArrowSquareOut\"\nexport { Sparkle } from \"./Sparkle\"\nexport { Flask } from \"./Flask\"\nexport { ArrowRight } from \"./ArrowRight\"\nexport { Browser } from \"./Browser\"\nexport { ShieldStar } from \"./ShieldStar\"\nexport { SealWarning } from \"./SealWarning\"\nexport { ChatCircleText } from \"./ChatCircleText\"\nexport { GitBranch } from \"./GitBranch\"\nexport { GithubLogo } from \"./GithubLogo\"\nexport { CaretRight } from \"./CaretRight\"\n"
  },
  {
    "path": "docs/components/InkeepSearch/index.tsx",
    "content": "import { useState, useCallback } from \"react\"\nimport { InkeepCustomTrigger, InkeepCustomTriggerProps } from \"@inkeep/widgets\"\nimport useInkeepSettings from \"@/utils/useInkeepSettings\"\nimport { Sparkle } from \"@/icons\"\n\nexport function InkeepTrigger() {\n  const [isOpen, setIsOpen] = useState(false)\n\n  const handleClose = useCallback(() => {\n    setIsOpen(false)\n  }, [])\n\n  const { baseSettings, aiChatSettings, modalSettings } = useInkeepSettings()\n\n  const inkeepCustomTriggerProps: InkeepCustomTriggerProps = {\n    isOpen,\n    onClose: handleClose,\n    baseSettings,\n    aiChatSettings,\n    modalSettings,\n  }\n\n  return (\n    <div>\n      <button\n        className=\"flex items-center gap-2 rounded-lg bg-black/[.05] px-3 py-1.5 text-base leading-tight text-gray-800 transition-colors md:text-sm dark:bg-gray-50/10 dark:text-gray-200\"\n        onClick={() => setIsOpen(!isOpen)}\n      >\n        <Sparkle className=\"size-4\" />\n        <span className=\"hidden md:inline xl:hidden\">AI</span>\n        <span className=\"hidden xl:inline\">Ask AI</span>\n      </button>\n      <InkeepCustomTrigger {...inkeepCustomTriggerProps} />\n    </div>\n  )\n}\n"
  },
  {
    "path": "docs/components/Link/index.tsx",
    "content": "import { ChildrenProps } from \"../../utils/types\"\n\nexport function Link({\n  children,\n  ...rest\n}: ChildrenProps & { href: string; className?: string; target?: string }) {\n  return (\n    <a\n      className=\"font-medium text-sky-600 no-underline dark:text-sky-500\"\n      {...rest}\n    >\n      {children}\n    </a>\n  )\n}\n"
  },
  {
    "path": "docs/components/ListDisclosure/index.tsx",
    "content": "import { useListDisclosure } from \"./useListDisclosure\"\nimport cx from \"classnames\"\n\ninterface Props {\n  children: React.ReactElement[]\n  limit: number\n  className?: string\n}\n\nexport function ListDisclosure({ children, limit, className = \"\" }: Props) {\n  const { displayLimit, handleCollapseAll, handleDisplayMore } =\n    useListDisclosure(limit)\n\n  const rendered = children.slice(0, displayLimit)\n  const isAllDisplayed = displayLimit >= children.length\n\n  return (\n    <>\n      <div\n        className={cx(\n          \"my-4 grid w-full grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\",\n          className\n        )}\n      >\n        {rendered}\n      </div>\n      <button\n        className=\"rounded-full bg-neutral-400 p-2 px-4 text-sm font-semibold text-white dark:bg-neutral-950\"\n        onClick={isAllDisplayed ? handleCollapseAll : handleDisplayMore}\n      >\n        {isAllDisplayed ? \"Collapse all\" : \"Show more\"}\n      </button>\n    </>\n  )\n}\n"
  },
  {
    "path": "docs/components/ListDisclosure/useListDisclosure.ts",
    "content": "import { useState } from \"react\"\n\nexport function useListDisclosure(initialLimit: number) {\n  const [displayLimit, setDisplayed] = useState<number>(initialLimit)\n\n  function handleDisplayMore() {\n    setDisplayed((s: number) => Number(s) + Number(initialLimit))\n  }\n\n  function handleCollapseAll() {\n    setDisplayed(initialLimit)\n  }\n\n  return {\n    handleDisplayMore,\n    handleCollapseAll,\n    displayLimit,\n  }\n}\n"
  },
  {
    "path": "docs/components/LogosMarquee/index.tsx",
    "content": "import { useEffect, useState } from \"react\"\nimport dynamic from \"next/dynamic\"\nimport manifest from \"@/data/manifest.json\"\n\nconst Logo = dynamic(() => import(\"./logo\").then((mod) => mod.Logo), {\n  ssr: false,\n})\n\nconst clamp = (min: number, num: number, max: number) =>\n  Math.min(Math.max(num, min), max)\n\nfunction changeScale() {\n  if (typeof window !== \"undefined\") {\n    const width = window.innerWidth\n    return clamp(40, Number((40 + width * 0.01).toFixed(2)), 80)\n  }\n}\n\nfunction changeLogoCount() {\n  if (typeof window !== \"undefined\") {\n    const width = window.innerWidth\n    return clamp(10, Number((10 + width * 0.004).toFixed(0)), 30)\n  }\n}\n\nexport const LogosMarquee = () => {\n  const [scale, setScale] = useState(changeScale)\n  const [logoCount, setLogoCount] = useState(changeLogoCount)\n\n  useEffect(() => {\n    // Window resize handling\n    function handleEvent() {\n      setScale(changeScale)\n      setLogoCount(changeLogoCount)\n    }\n\n    window.addEventListener(\"resize\", handleEvent)\n    return () => window.removeEventListener(\"resize\", handleEvent)\n  }, [])\n\n  return (\n    <div className=\"pointer-events-none absolute left-0 top-0 h-full w-full opacity-30\">\n      {Object.entries(manifest.providersOAuth)\n        .sort(() => Math.random() - 0.5)\n        .filter((_, i) => i < logoCount!)\n        .map(([id, name]) => (\n          <Logo providerId={id} name={name} scale={scale} key={id} />\n        ))}\n    </div>\n  )\n}\n"
  },
  {
    "path": "docs/components/LogosMarquee/logo.tsx",
    "content": "import { motion } from \"framer-motion\"\n\nconst logoSizePx = 96\n\nfunction randomFloat(min: number, max: number): number {\n  const randomValue = Math.random() * (max - min) + min\n  return Number(randomValue.toFixed(2))\n}\n\nexport const Logo = ({ providerId: id, name, scale }) => {\n  return (\n    <motion.div\n      initial={{ x: `${randomFloat(-40, 40)}vw` }}\n      animate={{ x: \"100vw\" }}\n      transition={{\n        delay: randomFloat(-10, 10),\n        duration: randomFloat(40, 80),\n        ease: \"linear\",\n        repeat: Infinity,\n        repeatType: \"loop\",\n      }}\n      style={{\n        width: (scale ?? 60) * randomFloat(0.8, 1.2),\n      }}\n    >\n      <motion.img\n        src={`/img/providers/${id}.svg`}\n        className=\"animate-orbit opacity-40 grayscale dark:invert\"\n        style={{\n          // @ts-expect-error\n          \"--duration\": randomFloat(20, 30),\n          \"--radius\": randomFloat(10, 20),\n        }}\n        width={logoSizePx}\n        height={logoSizePx}\n        alt={`${name} logo`}\n      />\n    </motion.div>\n  )\n}\n"
  },
  {
    "path": "docs/components/OAuthProviderInstructions/OAuthProviderSelect.tsx",
    "content": "import {\n  Combobox,\n  ComboboxItem,\n  ComboboxPopover,\n  ComboboxProvider,\n} from \"@ariakit/react\"\nimport dynamic from \"next/dynamic\"\nimport { Link } from \"@/components/Link\"\nimport manifest from \"@/data/manifest.json\"\nimport {\n  PreviewProviders,\n  type Provider,\n} from \"@/components/SearchBarProviders/PreviewProviders\"\nimport { useSelectCombobox } from \"@/hooks/use-select-combobox\"\n\nconst OAuthProviderInstructions = dynamic(() =>\n  import(\"./content\").then((mod) => mod.OAuthInstructions)\n)\n\nconst previewProviders: Provider[] = [\n  { id: \"google\", name: \"Google\" },\n  { id: \"github\", name: \"GitHub\" },\n  { id: \"twitter\", name: \"Twitter\" },\n  { id: \"keycloak\", name: \"Keycloak\" },\n  { id: \"okta\", name: \"Okta\" },\n]\n\nconst items = Object.entries(manifest.providersOAuth).map(([id, name]) => ({\n  id,\n  name,\n}))\n\nexport function OAuthProviderSelect() {\n  const {\n    selectedItem,\n    filteredItems,\n    hasMatchItem,\n    handleChange,\n    handleSelect,\n  } = useSelectCombobox({\n    items,\n  })\n\n  return (\n    <div className=\"mt-8\">\n      <ComboboxProvider\n        value={selectedItem.name}\n        selectedValue={selectedItem.id}\n      >\n        <Combobox\n          placeholder=\"Search for your favorite OAuth provider\"\n          className=\"w-full rounded-md border-2 border-gray-200 bg-neutral-100 px-4 py-2 font-medium text-neutral-800 shadow-sm md:w-96 dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-300\"\n          value={selectedItem.name}\n          onChange={handleChange}\n        />\n        <ComboboxPopover\n          gutter={4}\n          sameWidth\n          hideOnEscape\n          hideOnInteractOutside\n          className=\"z-50 mt-1 max-h-72 overflow-y-scroll rounded-md bg-neutral-100 p-2 empty:hidden dark:bg-neutral-900\"\n        >\n          {filteredItems.map((item) => (\n            <ComboboxItem\n              className=\"flex cursor-pointer flex-row items-center gap-4 px-2 py-2 aria-selected:bg-violet-200 dark:aria-selected:bg-violet-500 dark:aria-selected:text-neutral-900\"\n              value={item.name}\n              key={item.name}\n              onClick={() => handleSelect(item)}\n            >\n              <img\n                src={`/img/providers/${item.id}.svg`}\n                className=\"h-6 w-6 rounded-sm\"\n              />{\" \"}\n              {item.name}\n            </ComboboxItem>\n          ))}\n        </ComboboxPopover>\n        {!selectedItem.name && (\n          <>\n            <p className=\"mt-8 rounded-md\">\n              Or jump directly to one of the popular ones below.\n            </p>\n            <PreviewProviders\n              className=\"mt-8 flex flex-row gap-6 overflow-x-scroll pb-8\"\n              providers={previewProviders}\n              onSelected={handleSelect}\n            />\n          </>\n        )}\n        {!hasMatchItem && filteredItems.length === 0 && (\n          <p className=\"mt-6 rounded-md bg-violet-100 px-4 py-2 dark:bg-violet-300/50 dark:text-neutral-900\">\n            Can't find the OAuth provider you're looking for? You can always{\" \"}\n            <Link href=\"/guides/configuring-oauth-providers#adding-a-new-built-in-provider\">\n              build your own\n            </Link>\n            .\n          </p>\n        )}\n      </ComboboxProvider>\n      {hasMatchItem && (\n        <OAuthProviderInstructions providerId={selectedItem.id} />\n      )}\n    </div>\n  )\n}\n"
  },
  {
    "path": "docs/components/OAuthProviderInstructions/content/components/SetupCode.tsx",
    "content": "import { Code } from \"@/components/Code\"\nimport { Pre, Code as NXCode } from \"nextra/components\"\nimport { TSIcon } from \"./TSIcon\"\n\ninterface Props {\n  providerSymbol: string\n  providerId: string\n  highlight: (code: string) => string\n}\n\nexport function SetupCode({ providerId, providerSymbol, highlight }: Props) {\n  return (\n    <Code>\n      <Code.Next>\n        In Next.js we recommend setting up your configuration in a file in the\n        root of your repository, like at <code>auth.ts</code>.\n        <br />\n        <Pre\n          data-filename=\"./auth.ts\"\n          data-theme=\"default\"\n          data-copy=\"\"\n          data-language=\"tsx\"\n          icon={TSIcon}\n          className=\"px-4\"\n          dangerouslySetInnerHTML={{\n            __html: highlight(`\nimport NextAuth from \"next-auth\"\nimport ${providerSymbol} from \"next-auth/providers/${providerId}\"\n \nexport const { handlers, signIn, signOut, auth } = NextAuth({\n  providers: [${providerSymbol}],\n})`),\n          }}\n        />\n        <br />\n        Add the <code>handlers</code> which <code>NextAuth</code> returns to\n        your <code>api/auth/[...nextauth]/route.ts</code> file so that Auth.js\n        can run on any incoming request.\n        <Pre\n          data-filename=\"./app/api/auth/[...nextauth]/route.ts\"\n          data-theme=\"default\"\n          data-copy=\"\"\n          data-language=\"tsx\"\n          className=\"px-4\"\n          icon={TSIcon}\n          dangerouslySetInnerHTML={{\n            __html: highlight(`\nimport { handlers } from \"@/auth\"\nexport const { GET, POST } = handlers\n`),\n          }}\n        />\n      </Code.Next>\n      <Code.Qwik>\n        We recommend setting up your configuration in{\" \"}\n        <code>/src/routes/plugin@auth.ts</code> file.\n        <Pre\n          data-filename=\"/src/routes/plugin@auth.ts\"\n          data-theme=\"default\"\n          data-copy=\"\"\n          data-language=\"tsx\"\n          className=\"px-4\"\n          icon={TSIcon}\n          dangerouslySetInnerHTML={{\n            __html: highlight(`\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport ${providerSymbol} from \"@auth/qwik/providers/${providerId}\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$({\n  providers: [${providerSymbol}],\n}) `),\n          }}\n        />\n      </Code.Qwik>\n      <Code.Svelte>\n        In SvelteKit you should also setup your Auth.js configuration in a file\n        at <code>/src/auth.ts</code>.\n        <br />\n        <Pre\n          data-filename=\"./src/auth.ts\"\n          data-theme=\"default\"\n          data-copy=\"\"\n          data-language=\"tsx\"\n          className=\"px-4\"\n          icon={TSIcon}\n          dangerouslySetInnerHTML={{\n            __html: highlight(`\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport ${providerSymbol} from \"@auth/sveltekit/providers/${providerId}\"\n \nexport const { handle, signIn } = SvelteKitAuth({\n  providers: [${providerSymbol}],\n}) `),\n          }}\n        />\n        <br />\n        Add the <code>handler</code> which <code>SvelteKitAuth</code> returns to\n        your <code>hooks.server.ts</code> file so that Auth.js can run on any\n        incoming request.\n        <Pre\n          data-filename=\"./src/hooks.server.ts\"\n          data-theme=\"default\"\n          data-copy=\"\"\n          data-language=\"tsx\"\n          className=\"px-4\"\n          icon={TSIcon}\n          dangerouslySetInnerHTML={{\n            __html: highlight(`export { handle } from \"./auth\"`),\n          }}\n        />\n        <br />\n        Finally, using your <code>+layout.server.ts</code> we can add the{\" \"}\n        <code>session</code> object onto the <code>$page</code> store so that\n        the session is easy to access in your routes and components. For\n        example, on <code>$page.data.session</code>.\n        <Pre\n          data-filename=\"./src/routes/+layout.server.ts\"\n          data-theme=\"default\"\n          data-copy=\"\"\n          data-language=\"tsx\"\n          className=\"px-4\"\n          icon={TSIcon}\n          dangerouslySetInnerHTML={{\n            __html: highlight(`\nimport type { LayoutServerLoad } from \"./$types\"\n \nexport const load: LayoutServerLoad = async (event) => {\n  const session = await event.locals.auth()\n \n  return {\n    session,\n  }\n}`),\n          }}\n        />\n      </Code.Svelte>\n      <Code.Express>\n        <Pre\n          data-filename=\"./src/routes/auth.route.ts\"\n          data-theme=\"default\"\n          data-copy=\"\"\n          data-language=\"tsx\"\n          className=\"px-4\"\n          icon={TSIcon}\n          dangerouslySetInnerHTML={{\n            __html: highlight(`\nimport { ExpressAuth } from \"@auth/express\"\nimport ${providerSymbol} from \"@auth/express/providers/${providerId}\"\nimport express from \"express\"\n \nconst app = express()\n \n// If app is served through a proxy, trust the proxy to allow HTTPS protocol to be detected\napp.set('trust proxy', true)\napp.use(\"/auth/*\", ExpressAuth({ providers: [ ${providerSymbol} ] }))\n`),\n          }}\n        />\n      </Code.Express>\n    </Code>\n  )\n}\n"
  },
  {
    "path": "docs/components/OAuthProviderInstructions/content/components/SignInCode.tsx",
    "content": "import { Code } from \"@/components/Code\"\nimport { Pre } from \"nextra/components\"\nimport { TSIcon } from \"./TSIcon\"\n\ninterface Props {\n  providerName: string\n  providerId: string\n  highlight: (code: string) => string\n}\n\nexport function SignInCode({ providerId, providerName, highlight }: Props) {\n  return (\n    <Code>\n      <Code.Next>\n        <Pre\n          data-filename=\"./components/sign-in.tsx\"\n          data-theme=\"default\"\n          data-copy=\"\"\n          data-language=\"tsx\"\n          className=\"px-4\"\n          icon={TSIcon}\n          dangerouslySetInnerHTML={{\n            __html: highlight(`\nimport { signIn } from \"@/auth\"\n \nexport default function SignIn() {\n  return (\n    <form\n      action={async () => {\n        \"use server\"\n        await signIn(\"${providerId}\")\n      }}\n    >\n      <button type=\"submit\">Signin with ${providerName}</button>\n    </form>\n  )\n} `),\n          }}\n        />\n      </Code.Next>\n      <Code.NextClient>\n        <Pre\n          data-filename=\"./components/sign-in.tsx\"\n          data-theme=\"default\"\n          data-copy=\"\"\n          data-language=\"tsx\"\n          className=\"px-4\"\n          icon={TSIcon}\n          dangerouslySetInnerHTML={{\n            __html: highlight(`\n\"use client\"\n\nimport { signIn } from \"next-auth/react\"\n \nexport default function SignIn() {\n  return <button onClick={() => signIn(\"${providerId}\")}></button>\n}\n`),\n          }}\n        />\n      </Code.NextClient>\n      <Code.Qwik>\n        With Qwik we can do a server-side login with Form action, or a more\n        simple client-side login via submit method.\n        <Pre\n          data-filename=\"./components/sign-in.tsx\"\n          data-theme=\"default\"\n          data-copy=\"\"\n          data-language=\"tsx\"\n          className=\"px-4\"\n          icon={TSIcon}\n          dangerouslySetInnerHTML={{\n            __html: highlight(`\nimport { component$ } from \"@builder.io/qwik\"\nimport { Form } from \"@builder.io/qwik-city\"\nimport { useSignIn } from \"./plugin@auth\"\n\nexport default component$(() => {\n  const signInSig = useSignIn()\n\n  return (\n    <>\n      {/* server-side login with Form action */}\n      <Form action={signInSig}>\n        <input type=\"hidden\" name=\"providerId\" value=\"${providerId}\" />\n        <input\n          type=\"hidden\"\n          name=\"options.redirectTo\"\n          value=\"/\"\n        />\n        <button>Sign In</button>\n      </Form>\n\n      {/* submit method */}\n      <Link\n        onClick$={() => signInSig.submit({ redirectTo: \"/\" })}\n      >\n        SignIn\n      </Link>\n    </>\n  )\n}) `),\n          }}\n        />\n      </Code.Qwik>\n      <Code.Svelte>\n        With SvelteKit we can do a server-side login with Form Actions, or a\n        more simple client-side login via links and redirects.\n        <div className=\"my-4 text-xl font-semibold\">Client-side login</div>\n        <Pre\n          data-theme=\"default\"\n          data-copy=\"\"\n          data-language=\"tsx\"\n          className=\"px-4\"\n          icon={TSIcon}\n          data-filename=\"src/routes/+page.svelte\"\n          dangerouslySetInnerHTML={{\n            __html: highlight(`\n<script lang=\"ts\">\n   import { signIn } from \"@auth/sveltekit/client\"\n</script>\n \n<div>\n  <img src=\"/img/logo.svg\" alt=\"Company Logo\" />\n  <button on:click={signIn} />\n</div> `),\n          }}\n        />\n        <div className=\"my-4 text-xl font-semibold\">Server-side login</div>\n        <p>\n          First, we need a SvelteKit route to handle the <code>POST</code>{\" \"}\n          requests to the\n          <code>/signin</code> route\n        </p>\n        <Pre\n          data-theme=\"default\"\n          data-copy=\"\"\n          data-language=\"tsx\"\n          className=\"px-4\"\n          icon={TSIcon}\n          data-filename=\"src/routes/signin/+page.server.ts\"\n          dangerouslySetInnerHTML={{\n            __html: highlight(`import { signIn } from \"../../auth\"\nimport type { Actions } from \"./$types\"\nexport const actions: Actions = { default: signIn }\n`),\n          }}\n        />\n        <p className=\"mt-4\">\n          Finally, we can use the exported <code>SignIn</code> Svelte component\n          to add a button to our UI that will attempt a server-side login.\n        </p>\n        <Pre\n          data-theme=\"default\"\n          data-copy=\"\"\n          data-language=\"tsx\"\n          className=\"px-4\"\n          icon={TSIcon}\n          data-filename=\"src/routes/+page.svelte\"\n          dangerouslySetInnerHTML={{\n            __html: highlight(`\n<script lang=\"ts\">\n   import { SignIn } from \"@auth/sveltekit/components\"\n</script>\n \n<div>\n  <img src=\"/img/logo.svg\" alt=\"Company Logo\" />\n  <SignIn provider=\"${providerId}\" signInPage=\"signin\" />\n</div> `),\n          }}\n        />\n      </Code.Svelte>\n    </Code>\n  )\n}\n"
  },
  {
    "path": "docs/components/OAuthProviderInstructions/content/components/StepTitle.tsx",
    "content": "interface Props {\n  children: React.ReactNode\n  count: number\n}\n\nexport function StepTitle({ children, count }: Props) {\n  return (\n    <h3\n      className={`relative mt-8 text-2xl font-semibold tracking-tight text-slate-900 dark:text-slate-100`}\n    >\n      <span\n        className=\"absolute rounded-full\"\n        style={{\n          left: \"-2.65rem\",\n          color: \"#a3a3a3\",\n          backgroundColor: \"#f3f4f6\",\n          top: \"52%\",\n          transform: \"translateY(-50%)\",\n          fontSize: \"1rem\",\n          width: \"33px\",\n          height: \"33px\",\n          textAlign: \"center\",\n        }}\n      >\n        {count}\n      </span>\n      {children}\n    </h3>\n  )\n}\n"
  },
  {
    "path": "docs/components/OAuthProviderInstructions/content/components/TSIcon.tsx",
    "content": "export function TSIcon() {\n  return (\n    <svg\n      width=\"24\"\n      height=\"24\"\n      viewBox=\"0 0 24 24\"\n      fill=\"currentColor\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className=\"h-3.5 w-auto shrink-0\"\n    >\n      <path d=\"M0 2.66667V21.3333C0 22.8067 1.19333 24 2.66667 24H21.3333C22.8067 24 24 22.8067 24 21.3333V2.66667C24 1.19333 22.8067 0 21.3333 0H2.66667C1.19333 0 0 1.19333 0 2.66667ZM14.2213 12.6013H11.3973V21.3333H9.12133V12.6013H6.356V10.6667H14.2213V12.6013ZM14.664 20.8347V18.5C14.664 18.5 15.9387 19.4613 17.4693 19.4613C19 19.4613 18.94 18.4613 18.94 18.324C18.94 16.872 14.6053 16.872 14.6053 13.656C14.6053 9.28133 20.9213 11.008 20.9213 11.008L20.8427 13.0867C20.8427 13.0867 19.784 12.38 18.5867 12.38C17.3907 12.38 16.9587 12.9493 16.9587 13.5573C16.9587 15.1267 21.3333 14.9693 21.3333 18.128C21.3333 22.992 14.664 20.8347 14.664 20.8347Z\" />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "docs/components/OAuthProviderInstructions/content/index.tsx",
    "content": "import { useEffect, useState } from \"react\"\nimport { type Highlighter, createHighlighter } from \"shiki\"\nimport cx from \"classnames\"\nimport { Callout, Pre, Code as NXCode } from \"nextra/components\"\nimport { StepTitle } from \"./components/StepTitle\"\nimport { SetupCode } from \"./components/SetupCode\"\nimport { SignInCode } from \"./components/SignInCode\"\nimport { Link } from \"@/components/Link\"\nimport { Code } from \"@/components/Code\"\nimport manifest from \"@/data/manifest.json\"\n\ninterface Props {\n  providerId: string\n  disabled?: boolean\n}\n\nexport function OAuthInstructions({ providerId, disabled = false }: Props) {\n  const [highlighter, setHighlighter] = useState<Highlighter | null>(null)\n  useEffect(() => {\n    ;(async () => {\n      const hl = await createHighlighter({\n        themes: [\"github-light\", \"github-dark\"],\n        langs: [\"ts\", \"tsx\", \"bash\"],\n      })\n      setHighlighter(hl)\n    })()\n  }, [])\n\n  const highlight = (code: string): string => {\n    if (!highlighter) return \"\"\n    return highlighter.codeToHtml(code, {\n      lang: \"tsx\",\n      themes: {\n        light: \"github-light\",\n        dark: \"github-dark\",\n      },\n    })\n  }\n\n  const providerName = manifest.providersOAuth[providerId]\n  const providerSymbol = providerName.replace(/\\s+/g, \"\")\n  const envVars = [\n    `AUTH_${providerId.toUpperCase().replace(/-/gi, \"_\")}_ID={CLIENT_ID}`,\n    `AUTH_${providerId.toUpperCase().replace(/-/gi, \"_\")}_SECRET={CLIENT_SECRET}`,\n  ]\n  if (manifest.requiresIssuer.includes(providerId)) {\n    envVars.push(\n      `AUTH_${providerId.toUpperCase().replace(/-/gi, \"_\")}_ISSUER={ISSUER_URL}`\n    )\n  }\n  const envString = `\\n${envVars.join(\"\\n\")}\\n`\n\n  return (\n    <div\n      className={cx(\"nextra-steps mb-12 ml-4 dark:border-neutral-800\", {\n        \"pointer-events-none opacity-40\": disabled,\n      })}\n    >\n      {/* Step 1 */}\n      <StepTitle count={1}>\n        Register OAuth App in {providerName}'s dashboard\n      </StepTitle>\n      <p className=\"mt-6 leading-7 first:mt-0\">\n        First you have to setup an OAuth application on the {providerName}{\" \"}\n        developers dashboard.\n      </p>\n      <Callout>\n        If you haven’t used OAuth before, you can read the beginners\n        step-by-step guide on{\" \"}\n        <Link href=\"/guides/configuring-github\">\n          how to setup \"Sign in with GitHub\" with Auth.js\n        </Link>\n        .\n      </Callout>\n      <p className=\"mt-6 leading-7 first:mt-0\">\n        When registering an OAuth application on {providerName}, they will all\n        ask you to enter your application’s callback URL. See below for the\n        callback URL you must insert based on your framework.\n      </p>\n      <h4 className=\"-mb-3 mt-4 text-lg font-bold\">Callback URL</h4>\n      <Code>\n        <Code.Next>\n          <Pre data-copy=\"\">\n            <NXCode>\n              <span>{`[origin]/api/auth/callback/${providerId}`}</span>\n            </NXCode>\n          </Pre>\n        </Code.Next>\n        <Code.Qwik>\n          <Pre data-copy=\"\">\n            <NXCode>\n              <span>{`[origin]/auth/callback/${providerId}`}</span>\n            </NXCode>\n          </Pre>\n        </Code.Qwik>\n        <Code.Svelte>\n          <Pre data-copy=\"\">\n            <NXCode>\n              <span>{`[origin]/auth/callback/${providerId}`}</span>\n            </NXCode>\n          </Pre>\n        </Code.Svelte>\n        <Code.Express>\n          <Pre data-copy=\"\">\n            <NXCode>\n              <span>{`[origin]/auth/callback/${providerId}`}</span>\n            </NXCode>\n          </Pre>\n        </Code.Express>\n      </Code>\n      <Callout type=\"info\">\n        Many providers only allow you to register one callback URL at a time.\n        Therefore, if you want to have an active OAuth configuration for\n        development and production environments, you'll need to register a\n        second OAuth app in the {providerName} dashboard for the other\n        environment(s).\n      </Callout>\n      {/* Step 2 */}\n      <StepTitle count={2}>Setup Environment Variables</StepTitle>\n      <p className=\"mt-6 leading-7 first:mt-0\">\n        Once registered, you should receive a{\" \"}\n        {manifest.requiresIssuer.includes(providerId) ? (\n          <>\n            <strong>Client ID</strong>, <strong>Client Secret</strong> and{\" \"}\n            <strong>Issuer URL</strong>\n          </>\n        ) : (\n          <>\n            <strong>Client ID</strong> and <strong>Client Secret</strong>\n          </>\n        )}\n        . Add those in your application environment file:\n      </p>\n      <Code>\n        <Code.Next>\n          <Pre data-copy=\"\" data-filename=\".env.local\">\n            <div className=\"px-4\">\n              <div\n                dangerouslySetInnerHTML={{\n                  __html: highlight(envString),\n                }}\n              />\n            </div>\n          </Pre>\n        </Code.Next>\n        <Code.Qwik>\n          <Pre data-copy=\"\" data-filename=\".env\">\n            <div className=\"px-4\">\n              <div\n                dangerouslySetInnerHTML={{\n                  __html: highlight(envString),\n                }}\n              />\n            </div>\n          </Pre>\n        </Code.Qwik>\n        <Code.Svelte>\n          <Pre data-copy=\"\" data-filename=\".env\">\n            <div className=\"px-4\">\n              <div\n                dangerouslySetInnerHTML={{\n                  __html: highlight(envString),\n                }}\n              />\n            </div>\n          </Pre>\n        </Code.Svelte>\n        <Code.Express>\n          <Pre data-copy=\"\" data-filename=\".env\">\n            <div className=\"px-4\">\n              <div\n                dangerouslySetInnerHTML={{\n                  __html: highlight(envString),\n                }}\n              />\n            </div>\n          </Pre>\n          <p className=\"mt-2 leading-7 first:mt-0\">\n            Assuming{\" \"}\n            <Link href=\"https://www.npmjs.com/package/dotenv\">\n              <NXCode>dotenv</NXCode>\n            </Link>{\" \"}\n            is installed or you're using{\" \"}\n            <Link href=\"https://nodejs.org/dist/latest-v20.x/docs/api/cli.html#--env-fileconfig\">\n              Node 20 <NXCode>.env</NXCode> file feature\n            </Link>\n            .\n          </p>\n        </Code.Express>\n      </Code>\n      <p className=\"mt-6 leading-7 first:mt-0\">\n        Auth.js will automatically pick up these if formatted like the example\n        above. You can{\" \"}\n        <Link href=\"/guides/environment-variables#oauth-variables\">\n          also use a different name for the environment variables\n        </Link>{\" \"}\n        if needed, but then you’ll need to pass them to the provider manually.\n      </p>\n      {/* Step 3 */}\n      <StepTitle count={3}>Setup Provider</StepTitle>\n      <p className=\"mt-6 leading-7 first:mt-0\">\n        Let’s enable {providerName} as a sign in option in our Auth.js\n        configuration. You’ll have to import the <NXCode>{providerName}</NXCode>{\" \"}\n        provider from the package and pass it to the <NXCode>providers</NXCode>{\" \"}\n        array we setup earlier in the Auth.js config file:\n      </p>\n      <SetupCode\n        providerId={providerId}\n        providerSymbol={providerSymbol}\n        highlight={highlight}\n      />\n      {/* Step 4 */}\n      <StepTitle count={4}>Add Signin Button</StepTitle>\n      <p className=\"mt-6 leading-7 first:mt-0\">\n        Next, we can add a signin button somewhere in your application like the\n        Navbar. It will trigger Auth.js sign in when clicked.\n      </p>\n      <SignInCode\n        providerId={providerId}\n        providerName={providerName}\n        highlight={highlight}\n      />\n      {/* Step 5 */}\n      <StepTitle count={5}>Ship it!</StepTitle>\n      <p className=\"mt-6 leading-7 first:mt-0\">\n        Click the “Sign in with {providerName}\" button and if all went well, you\n        should be redirected to {providerName} and once authenticated,\n        redirected back to the app!\n      </p>\n      <Callout>\n        You can build your own Signin, Signout, etc. pages to match the style of\n        your application, check out{\" \"}\n        <Link href=\"/getting-started/session-management/custom-pages\">\n          session management\n        </Link>{\" \"}\n        for more details.\n      </Callout>\n      <p className=\"mt-6 leading-7 first:mt-0\">\n        For more information on this provider check out the detailed{\" \"}\n        {providerName} provider{\" \"}\n        <Link href={`/getting-started/providers/${providerId}`}>docs page</Link>\n        .\n      </p>\n    </div>\n  )\n}\n"
  },
  {
    "path": "docs/components/OAuthProviderInstructions/index.tsx",
    "content": "export { OAuthProviderSelect } from \"./OAuthProviderSelect\"\n"
  },
  {
    "path": "docs/components/RichTabs/index.tsx",
    "content": "import { List, Trigger, Root, Content } from \"@radix-ui/react-tabs\"\nimport type {\n  TabsListProps,\n  TabsTriggerProps,\n  TabsContentProps,\n  TabsProps,\n} from \"@radix-ui/react-tabs\"\nimport cx from \"classnames\"\nimport { useRichTabs } from \"./useRichTabs\"\nimport { useState } from \"react\"\n\nRichTabs.List = function TabsList({ className, ...rest }: TabsListProps) {\n  return (\n    <List\n      {...rest}\n      className={cx(\"flex flex-row items-center justify-start\", className)}\n    />\n  )\n}\n\nRichTabs.Trigger = function TabsTrigger({\n  className,\n  orientation = \"horizontal\",\n  ...rest\n}: TabsTriggerProps & { orientation?: TabsProps[\"orientation\"] }) {\n  return (\n    <Trigger\n      {...rest}\n      className={cx(\n        \"relative flex h-24 w-48 flex-col items-center justify-between border-solid border-slate-200 bg-slate-50 text-sm font-semibold transition-all duration-300 aria-selected:top-px aria-selected:bg-white dark:border-neutral-800 dark:bg-neutral-900 dark:aria-selected:bg-neutral-700\",\n        className,\n        orientation === \"horizontal\"\n          ? \"rounded-tl-lg rounded-tr-lg border-l border-r border-t aria-selected:border-b-white\"\n          : \"rounded-md\"\n      )}\n    />\n  )\n}\n\nRichTabs.Content = function TabsContent({\n  className,\n  orientation = \"horizontal\",\n  ...rest\n}: TabsContentProps & { orientation?: TabsProps[\"orientation\"] }) {\n  return (\n    <Content\n      forceMount\n      {...rest}\n      className={cx(\n        'border border-solid border-slate-200 shadow-sm data-[state=\"inactive\"]:hidden dark:border-neutral-800',\n        className,\n        orientation === \"horizontal\"\n          ? \"rounded-bl-lg rounded-br-lg rounded-tr-lg\"\n          : \"rounded-md\"\n      )}\n    />\n  )\n}\n\ntype Props = TabsProps & { onTabChange?: (value: string) => void } & {\n  defaultValue: string\n  tabKey?: string\n}\n\nexport function RichTabs({\n  children,\n  className,\n  orientation = \"horizontal\",\n  defaultValue,\n  onTabChange,\n  tabKey,\n}: Props) {\n  const [value, setValue] = useState<string>(defaultValue)\n  const { handleValueChanged } = useRichTabs({\n    onTabChange,\n    value,\n    setValue,\n    defaultValue,\n    tabKey,\n  })\n\n  return (\n    <Root\n      className={cx(\"m-0 mt-2 rounded-lg px-0 pt-4\", className)}\n      orientation={orientation}\n      onValueChange={handleValueChanged}\n      value={value}\n    >\n      {children}\n    </Root>\n  )\n}\n"
  },
  {
    "path": "docs/components/RichTabs/useRichTabs.ts",
    "content": "import { useSearchParams, useRouter, usePathname } from \"next/navigation\"\n\ninterface Args {\n  onTabChange: ((value: string) => void) | undefined\n  defaultValue: string\n  value: string\n  persist?: boolean\n  tabKey?: string\n  setValue?: any\n}\n\nexport function useRichTabs({\n  onTabChange,\n  tabKey = \"tab\",\n  persist = true,\n  value,\n  setValue,\n}: Args) {\n  const router = useRouter()\n  const pathname = usePathname()\n  const searchParams = useSearchParams()\n  const searchParamsTab = searchParams?.get(tabKey)\n\n  // Handle searchParams\n  if (searchParamsTab && value !== searchParamsTab) {\n    router.push(pathname!)\n    setValue((prevVal: string) => {\n      if (prevVal !== searchParamsTab) {\n        return searchParamsTab\n      }\n    })\n    persist && window.localStorage.setItem(`authjs.${tabKey}`, value)\n  }\n\n  // TODO: Handle localStorage saved value\n  // if (!searchParamsTab && typeof window !== \"undefined\") {\n  //   const storedTab = window.localStorage.getItem(`authjs.${tabKey}`)\n  //   if (storedTab && storedTab !== value) {\n  //     setValue(storedTab)\n  //   }\n  // }\n  //\n  function handleValueChanged(value: string) {\n    setValue(value)\n    persist && window.localStorage.setItem(`authjs.${tabKey}`, value)\n    onTabChange && onTabChange(value)\n  }\n\n  return {\n    handleValueChanged,\n  }\n}\n"
  },
  {
    "path": "docs/components/Screenshot/index.tsx",
    "content": "import cx from \"classnames\"\nimport Image from \"next/image\"\n\nexport function Screenshot({ src, alt, full, className }) {\n  return (\n    <div\n      className={cx(\n        \"mt-6 flex justify-center overflow-hidden rounded-xl border border-zinc-200 shadow-lg\",\n        full ? \"bg-white\" : \"m-8 bg-zinc-100\",\n        className\n      )}\n    >\n      <Image\n        src={src}\n        alt={alt}\n        className={cx(\n          \"w-auto select-none bg-white\",\n          full ? \"\" : \"ring-1 ring-gray-200\"\n        )}\n      />\n    </div>\n  )\n}\n"
  },
  {
    "path": "docs/components/SearchBarProviders/PreviewProviders.tsx",
    "content": "export interface Provider {\n  id: string\n  name: string\n}\n\nexport interface PreviewProvidersProps {\n  className?: string\n  providers: Provider[]\n  onSelected: (provider: Provider) => void\n}\n\nexport function PreviewProviders({\n  className,\n  providers,\n  onSelected,\n}: PreviewProvidersProps) {\n  return (\n    <section className={className}>\n      {providers.map((provider) => (\n        <div\n          className=\"flex h-32 w-32 min-w-24 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950\"\n          key={provider.id}\n          role=\"button\"\n          onClick={() => onSelected(provider)}\n        >\n          <img\n            src={`/img/providers/${provider.id}.svg`}\n            className=\"mt-2 w-11\"\n          />\n          <div className=\"text-center text-sm\">{provider.name}</div>\n        </div>\n      ))}\n    </section>\n  )\n}\n"
  },
  {
    "path": "docs/hooks/use-select-combobox.ts",
    "content": "import { ChangeEvent, useState } from \"react\"\n\ninterface SelectComboboxValue {\n  id: string\n  name: string\n}\n\ninterface SelectComboboxProps {\n  defaultValue?: SelectComboboxValue\n  items: SelectComboboxValue[]\n}\n\nexport const useSelectCombobox = ({\n  defaultValue = { id: \"\", name: \"\" },\n  items,\n}: SelectComboboxProps) => {\n  const [selectedItem, setSelectedItem] =\n    useState<SelectComboboxValue>(defaultValue)\n  const [filteredItems, setFilteredItems] = useState(items)\n  const [hasMatchItem, setHasMatchItem] = useState(false)\n\n  const handleSelect = (value: SelectComboboxValue) => {\n    let hasMatchItem = false\n    setFilteredItems(\n      items.filter((item) => {\n        if (item.id === value.id) {\n          hasMatchItem = true\n        }\n        return item.name.toLowerCase().includes(value.name.toLowerCase())\n      })\n    )\n    setSelectedItem(value)\n    setHasMatchItem(hasMatchItem)\n  }\n\n  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n    const { value } = event.target\n    handleSelect({ id: value, name: value })\n  }\n\n  return {\n    selectedItem,\n    filteredItems,\n    handleSelect,\n    handleChange,\n    hasMatchItem,\n  }\n}\n"
  },
  {
    "path": "docs/next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n/// <reference types=\"next/navigation-types/compat/navigation\" />\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.\n"
  },
  {
    "path": "docs/next-sitemap.config.cjs",
    "content": "/** @type {import('next-sitemap').IConfig} */\nmodule.exports = {\n  siteUrl:\n    process.env.VERCEL_ENV === \"preview\"\n      ? process.env.VERCEL_URL\n      : \"https://authjs.dev\",\n  generateIndexSitemap: false,\n  generateRobotsTxt: true,\n}\n"
  },
  {
    "path": "docs/next.config.js",
    "content": "import nextra from \"nextra\"\n\nconst withNextra = nextra({\n  theme: \"nextra-theme-docs\",\n  themeConfig: \"./theme.config.tsx\",\n  defaultShowCopyCode: true,\n  codeHighlight: true,\n})\n\nexport default withNextra({\n  redirects: () => {\n    return [\n      {\n        source: \"/security.txt\",\n        destination: \"/.well-known/security.txt\",\n        permanent: true,\n      },\n      {\n        source: \"/new/provider-issue\",\n        destination:\n          \"https://github.com/nextauthjs/next-auth/issues/new?assignees=&labels=triage%2Cproviders&template=2_bug_provider.yml\",\n        permanent: true,\n      },\n      {\n        source: \"/new/github-discussions\",\n        destination:\n          \"https://github.com/nextauthjs/next-auth/discussions/categories/questions\",\n        permanent: true,\n      },\n      {\n        source: \"/:path(.*)\",\n        has: [{ type: \"host\", value: \"sveltekit.authjs.dev\" }],\n        destination: \"https://authjs.dev/reference/sveltekit\",\n        permanent: true,\n      },\n      {\n        source: \"/:path(.*)\",\n        has: [{ type: \"host\", value: \"solid-start.authjs.dev\" }],\n        destination: \"https://authjs.dev/reference/solid-start\",\n        permanent: true,\n      },\n      {\n        source: \"/:path(.*)\",\n        has: [{ type: \"host\", value: \"express.authjs.dev\" }],\n        destination: \"https://authjs.dev/reference/express\",\n        permanent: true,\n      },\n      {\n        source: \"/:path(.*)\",\n        has: [{ type: \"host\", value: \"nextjs.authjs.dev\" }],\n        destination: \"https://authjs.dev/reference/nextjs\",\n        permanent: true,\n      },\n      {\n        source: \"/:path(.*)\",\n        has: [{ type: \"host\", value: \"qwik.authjs.dev\" }],\n        destination: \"https://authjs.dev/reference/qwik\",\n        permanent: true,\n      },\n      {\n        source: \"/:path(.*)\",\n        has: [{ type: \"host\", value: \"cli.authjs.dev\" }],\n        destination: \"https://github.com/nextauthjs/cli\",\n        permanent: true,\n      },\n      {\n        source: \"/:path(.*)\",\n        has: [{ type: \"host\", value: \"errors.authjs.dev\" }],\n        destination: \"https://authjs.dev/reference/core/errors/:path*\",\n        permanent: true,\n      },\n      {\n        source: \"/:path(.*)\",\n        has: [{ type: \"host\", value: \"warnings.authjs.dev\" }],\n        destination: \"https://authjs.dev/reference/core/types#warningcode\",\n        permanent: true,\n      },\n      {\n        source: \"/\",\n        has: [{ type: \"host\", value: \"adapters.authjs.dev\" }],\n        destination: \"https://authjs.dev/getting-started/database\",\n        permanent: true,\n      },\n      {\n        source: \"/:path(.*)\",\n        has: [{ type: \"host\", value: \"adapters.authjs.dev\" }],\n        destination: \"https://authjs.dev/reference/adapter/:path*\",\n        permanent: true,\n      },\n      {\n        source: \"/\",\n        has: [{ type: \"host\", value: \"providers.authjs.dev\" }],\n        destination: \"https://authjs.dev/getting-started/authentication/oauth\",\n        permanent: true,\n      },\n      {\n        source: \"/:path(.*)\",\n        has: [{ type: \"host\", value: \"providers.authjs.dev\" }],\n        destination: \"https://authjs.dev/getting-started/providers/:path\",\n        permanent: true,\n      },\n      {\n        source: \"/reference/core/providers_:slug(.*)\",\n        destination: \"/reference/core/providers/:slug\",\n        permanent: true,\n      },\n      {\n        source: \"/\",\n        has: [{ type: \"host\", value: \"discord.authjs.dev\" }],\n        destination: \"https://discord.gg/kuv7wXkHY4\",\n        permanent: true,\n      },\n      {\n        source: \"/reference/next-auth:path(.*)\",\n        destination: \"/reference/nextjs:path(.*)\",\n        permanent: true,\n      },\n      {\n        source: \"/img/providers/:providerId*-dark.svg\",\n        destination: \"/img/providers/:providerId*.svg\",\n        permanent: true,\n      },\n      {\n        source: \"/reference/adapter/:path(.*)\",\n        destination: \"/getting-started/adapters/:path(.*)\",\n        permanent: true,\n      },\n      {\n        source: \"/getting-started/providers/email\",\n        destination: \"/getting-started/providers/nodemailer\",\n        permanent: true,\n      },\n      {\n        source: \"/guides/basics/role-based-access-control\",\n        destination: \"/guides/role-based-access-control\",\n        permanent: true,\n      },\n      {\n        source: \"/guides/basics/refresh-token-rotation\",\n        destination: \"/guides/refresh-token-rotation\",\n        permanent: true,\n      },\n      {\n        source: \"/getting-started/providers\",\n        destination: \"/getting-started/authentication/oauth\",\n        permanent: true,\n      },\n      {\n        source: \"/getting-started/providers/oauth-tutorial\",\n        destination: \"/getting-started/authentication/oauth\",\n        permanent: true,\n      },\n      {\n        source: \"/getting-started/providers/email-tutorial\",\n        destination: \"/getting-started/authentication/email\",\n        permanent: true,\n      },\n      {\n        source: \"/getting-started/providers/credentials-tutorial\",\n        destination: \"/getting-started/providers/credentials\",\n        permanent: true,\n      },\n      {\n        source: \"/guides/providers/email-http\",\n        destination: \"/guides/configuring-http-email\",\n        permanent: true,\n      },\n      {\n        source: \"/guides/upgrade-to-v5\",\n        destination: \"/getting-started/migrating-to-v5\",\n        permanent: true,\n      },\n      {\n        permanent: true,\n        source: \"/guides\",\n        destination: \"/guides/debugging\",\n      },\n    ]\n  },\n})\n"
  },
  {
    "path": "docs/package.json",
    "content": "{\n  \"name\": \"docs\",\n  \"description\": \"Auth.js Documentation\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"typedoc\": \"typedoc\",\n    \"dev\": \"TYPEDOC_SKIP_ADAPTERS=1 typedoc & next dev\",\n    \"dev:adapters\": \"typedoc & next dev\",\n    \"build\": \"typedoc && next build\",\n    \"start\": \"next start\",\n    \"lint\": \"eslint ./{components,pages,utils}\",\n    \"lint:fix\": \"eslint ./{components,pages,utils} --fix\",\n    \"build:sitemap\": \"next-sitemap --config next-sitemap.config.cjs\",\n    \"clean\": \"rm -rf .next build pages/reference/*.mdx pages/reference/**/*\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/nextauthjs/next-auth\"\n  },\n  \"author\": \"Auth.js Team (https://authjs.dev/contributors)\",\n  \"license\": \"ISC\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"homepage\": \"https://authjs.dev\",\n  \"dependencies\": {\n    \"@ariakit/react\": \"^0.4.13\",\n    \"@docsearch/react\": \"3\",\n    \"@inkeep/widgets\": \"^0.2.289\",\n    \"@next/third-parties\": \"^14.2.15\",\n    \"@radix-ui/react-accordion\": \"^1.2.1\",\n    \"@radix-ui/react-tabs\": \"^1.1.1\",\n    \"@vercel/analytics\": \"^1.3.1\",\n    \"@vercel/kv\": \"^1.0.1\",\n    \"algoliasearch\": \"^4.24.0\",\n    \"better-auth\": \"^1.3.18\",\n    \"classnames\": \"^2.5.1\",\n    \"framer-motion\": \"^11.11.8\",\n    \"next\": \"14.2.21\",\n    \"next-sitemap\": \"^4.2.3\",\n    \"nextra\": \"3.0.15\",\n    \"nextra-theme-docs\": \"3.0.15\",\n    \"react\": \"18.3.1\",\n    \"react-dom\": \"18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"20.12.7\",\n    \"@types/react\": \"18.2.78\",\n    \"autoprefixer\": \"^10.4.20\",\n    \"postcss\": \"^8.4.47\",\n    \"shiki\": \"^1.22.0\",\n    \"tailwindcss\": \"^3.4.13\",\n    \"typedoc\": \"^0.27.6\",\n    \"typedoc-plugin-markdown\": \"4.3.3\",\n    \"typedoc-plugin-mdn-links\": \"4.0.11\",\n    \"typedoc-plugin-no-inherit\": \"^1.5.0\"\n  }\n}\n"
  },
  {
    "path": "docs/pages/404.mdx",
    "content": "import { NotFoundPage } from \"nextra-theme-docs\"\nimport Image from \"next/image\"\n\n# Page Not Found\n\nimport ConfusedTravolta from \"../public/img/etc/confused-travolta.gif\"\n\n<div className=\"\">\n  <Image\n    src={ConfusedTravolta}\n    alt=\"Confused Travolta\"\n    unoptimized\n    className=\"mx-auto my-32 rounded-md\"\n    width={768}\n  />\n</div>\n\n<NotFoundPage />\n"
  },
  {
    "path": "docs/pages/_app.tsx",
    "content": "import \"./global.css\"\nimport { GoogleAnalytics } from \"@next/third-parties/google\"\nimport { Analytics } from \"@vercel/analytics/react\"\nimport Script from \"next/script\"\n\nexport default function App({ Component, pageProps }) {\n  if (process.env.NEXT_PUBLIC_VERCEL_ENV !== \"production\") {\n    return <Component {...pageProps} />\n  }\n\n  return (\n    <>\n      <Component {...pageProps} />\n      <GoogleAnalytics gaId=\"AW-11313383806\" />\n      <Analytics />\n      <Script id=\"twitter-pixel\" strategy=\"lazyOnload\">{`\n!function(e,t,n,s,u,a){e.twq||(s=e.twq=function(){s.exe?s.exe.apply(s,arguments):s.queue.push(arguments);\n},s.version='1.1',s.queue=[],u=t.createElement(n),u.async=!0,u.src='https://static.ads-twitter.com/uwt.js',\na=t.getElementsByTagName(n)[0],a.parentNode.insertBefore(u,a))}(window,document,'script');\ntwq('config','o6tnh');\ntwq('event', 'tw-o6tnh-oi8tp', {});\n      `}</Script>\n    </>\n  )\n}\n"
  },
  {
    "path": "docs/pages/_document.tsx",
    "content": "import Document, { Html, Head, Main, NextScript } from \"next/document\"\nimport { SkipNavLink } from \"nextra-theme-docs\"\n\nclass AuthDocument extends Document {\n  render() {\n    return (\n      <Html lang=\"en\">\n        <Head />\n        <body>\n          <SkipNavLink styled />\n          <Main />\n          <NextScript />\n        </body>\n      </Html>\n    )\n  }\n}\n\nexport default AuthDocument\n"
  },
  {
    "path": "docs/pages/_meta.js",
    "content": "export default {\n  index: {\n    title: \"Homepage\",\n    type: \"page\",\n    display: \"hidden\",\n    theme: {\n      breadcrumb: false,\n      sidebar: false,\n      footer: false,\n      layout: \"raw\",\n    },\n  },\n  \"getting-started\": {\n    title: \"Getting Started\",\n    type: \"page\",\n  },\n  guides: {\n    title: \"Guides\",\n    type: \"page\",\n  },\n  reference: {\n    title: \"API reference\",\n    type: \"page\",\n  },\n  concepts: {\n    title: \"Concepts\",\n    type: \"page\",\n  },\n  security: {\n    title: \"Security\",\n    type: \"page\",\n  },\n  contributors: {\n    title: \"Contributors\",\n    type: \"page\",\n    display: \"hidden\",\n    theme: {\n      typesetting: \"article\",\n    },\n  },\n  404: {\n    title: \"404\",\n    type: \"page\",\n    display: \"hidden\",\n    theme: {\n      typesetting: \"article\",\n    },\n  },\n}\n"
  },
  {
    "path": "docs/pages/animated-stars.css",
    "content": ".bg-animation {\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 100vw;\n  height: 100vh;\n}\n\n#stars {\n  width: 1px;\n  height: 1px;\n  background: transparent;\n  box-shadow:\n    117px 1613px #ce93d8,\n    1488px 635px #ce93d8,\n    944px 914px #ce93d8,\n    647px 277px #ce93d8,\n    1792px 1205px #ce93d8,\n    656px 1517px #ce93d8,\n    820px 1839px #ce93d8,\n    1153px 1400px #ce93d8,\n    870px 13px #ce93d8,\n    550px 702px #ce93d8,\n    1155px 1056px #ce93d8,\n    88px 1709px #ce93d8,\n    1450px 1090px #ce93d8,\n    1929px 457px #ce93d8,\n    1390px 905px #ce93d8,\n    1771px 269px #ce93d8,\n    1741px 669px #ce93d8,\n    432px 64px #ce93d8,\n    563px 996px #ce93d8,\n    1918px 1873px #ce93d8,\n    1845px 1211px #ce93d8,\n    231px 1503px #ce93d8,\n    37px 220px #ce93d8,\n    1970px 495px #ce93d8,\n    1812px 925px #ce93d8,\n    67px 1398px #ce93d8,\n    535px 279px #ce93d8,\n    1837px 829px #ce93d8,\n    1945px 685px #ce93d8,\n    1677px 1817px #ce93d8,\n    1317px 1415px #ce93d8,\n    1785px 905px #ce93d8,\n    1787px 1554px #ce93d8,\n    802px 1296px #ce93d8,\n    512px 1101px #ce93d8,\n    583px 1364px #ce93d8,\n    336px 558px #ce93d8,\n    979px 334px #ce93d8,\n    106px 792px #ce93d8,\n    204px 34px #ce93d8,\n    1845px 1763px #ce93d8,\n    445px 1599px #ce93d8,\n    386px 453px #ce93d8,\n    471px 952px #ce93d8,\n    1466px 1676px #ce93d8,\n    1885px 303px #ce93d8,\n    51px 1717px #ce93d8,\n    1211px 299px #ce93d8,\n    1546px 1887px #ce93d8,\n    1067px 33px #ce93d8,\n    1088px 1326px #ce93d8,\n    1938px 760px #ce93d8,\n    470px 648px #ce93d8,\n    1213px 269px #ce93d8,\n    1767px 78px #ce93d8,\n    977px 976px #ce93d8,\n    1926px 175px #ce93d8,\n    722px 1512px #ce93d8,\n    945px 227px #ce93d8,\n    1811px 99px #ce93d8,\n    1912px 1406px #ce93d8,\n    1602px 1243px #ce93d8,\n    610px 449px #ce93d8,\n    654px 1393px #ce93d8,\n    1930px 1193px #ce93d8,\n    258px 1184px #ce93d8,\n    89px 265px #ce93d8,\n    824px 1494px #ce93d8,\n    1506px 1435px #ce93d8,\n    1027px 753px #ce93d8,\n    1px 1197px #ce93d8,\n    530px 1161px #ce93d8,\n    864px 1555px #ce93d8,\n    1610px 1604px #ce93d8,\n    1035px 1114px #ce93d8,\n    1456px 133px #ce93d8,\n    1196px 1253px #ce93d8,\n    361px 1037px #ce93d8,\n    834px 351px #ce93d8,\n    436px 1676px #ce93d8,\n    1194px 1007px #ce93d8,\n    1141px 647px #ce93d8,\n    319px 454px #ce93d8,\n    937px 1769px #ce93d8,\n    1872px 1013px #ce93d8,\n    733px 643px #ce93d8,\n    1250px 511px #ce93d8,\n    189px 296px #ce93d8,\n    1639px 163px #ce93d8,\n    1584px 336px #ce93d8,\n    1912px 1343px #ce93d8,\n    1298px 1307px #ce93d8,\n    1750px 902px #ce93d8,\n    1129px 845px #ce93d8,\n    1899px 1470px #ce93d8,\n    1427px 232px #ce93d8,\n    1391px 838px #ce93d8,\n    1225px 1819px #ce93d8,\n    190px 1366px #ce93d8,\n    1865px 518px #ce93d8,\n    203px 1383px #ce93d8,\n    1455px 614px #ce93d8,\n    423px 354px #ce93d8,\n    1678px 1790px #ce93d8,\n    241px 608px #ce93d8,\n    1089px 730px #ce93d8,\n    1342px 38px #ce93d8,\n    1848px 249px #ce93d8,\n    1874px 1785px #ce93d8,\n    1040px 1837px #ce93d8,\n    751px 261px #ce93d8,\n    510px 1975px #ce93d8,\n    52px 795px #ce93d8,\n    1786px 1310px #ce93d8,\n    498px 712px #ce93d8,\n    190px 375px #ce93d8,\n    1341px 722px #ce93d8,\n    43px 1394px #ce93d8,\n    1821px 1687px #ce93d8,\n    106px 130px #ce93d8,\n    1717px 1978px #ce93d8,\n    168px 151px #ce93d8,\n    183px 740px #ce93d8,\n    945px 1381px #ce93d8,\n    669px 1170px #ce93d8,\n    1285px 1816px #ce93d8,\n    110px 1217px #ce93d8,\n    1623px 813px #ce93d8,\n    869px 647px #ce93d8,\n    867px 582px #ce93d8,\n    735px 1240px #ce93d8,\n    519px 1896px #ce93d8,\n    132px 156px #ce93d8,\n    1649px 193px #ce93d8,\n    241px 1109px #ce93d8,\n    643px 484px #ce93d8,\n    574px 1282px #ce93d8,\n    1952px 564px #ce93d8,\n    1978px 145px #ce93d8,\n    329px 903px #ce93d8,\n    1674px 617px #ce93d8,\n    1978px 558px #ce93d8,\n    1808px 1715px #ce93d8,\n    1526px 1238px #ce93d8,\n    475px 1330px #ce93d8,\n    810px 425px #ce93d8,\n    1709px 634px #ce93d8,\n    1658px 336px #ce93d8,\n    425px 194px #ce93d8,\n    352px 96px #ce93d8,\n    148px 180px #ce93d8,\n    1139px 1046px #ce93d8,\n    1809px 1233px #ce93d8,\n    1669px 171px #ce93d8,\n    263px 1394px #ce93d8,\n    534px 715px #ce93d8,\n    396px 1008px #ce93d8,\n    589px 1445px #ce93d8,\n    1190px 381px #ce93d8,\n    1709px 279px #ce93d8,\n    520px 891px #ce93d8,\n    1136px 1867px #ce93d8,\n    1280px 1233px #ce93d8,\n    836px 296px #ce93d8,\n    1348px 646px #ce93d8,\n    1539px 913px #ce93d8,\n    423px 781px #ce93d8,\n    1271px 1805px #ce93d8,\n    696px 564px #ce93d8,\n    1549px 804px #ce93d8,\n    303px 1555px #ce93d8,\n    1449px 1903px #ce93d8,\n    66px 687px #ce93d8,\n    1164px 856px #ce93d8,\n    1958px 1326px #ce93d8,\n    125px 157px #ce93d8,\n    508px 1669px #ce93d8,\n    465px 725px #ce93d8,\n    1925px 1440px #ce93d8,\n    405px 793px #ce93d8,\n    278px 110px #ce93d8,\n    1084px 1065px #ce93d8,\n    1077px 705px #ce93d8,\n    663px 1844px #ce93d8,\n    734px 263px #ce93d8,\n    870px 1761px #ce93d8,\n    103px 1169px #ce93d8,\n    1506px 1295px #ce93d8,\n    1883px 926px #ce93d8,\n    335px 1361px #ce93d8,\n    1126px 1284px #ce93d8,\n    257px 1165px #ce93d8,\n    837px 580px #ce93d8,\n    1211px 1362px #ce93d8,\n    1137px 1380px #ce93d8,\n    135px 632px #ce93d8,\n    1491px 1965px #ce93d8,\n    1098px 195px #ce93d8,\n    506px 417px #ce93d8,\n    693px 1243px #ce93d8,\n    622px 1862px #ce93d8,\n    1412px 1343px #ce93d8,\n    948px 1894px #ce93d8,\n    1315px 1363px #ce93d8,\n    754px 1098px #ce93d8,\n    1931px 930px #ce93d8,\n    1831px 342px #ce93d8,\n    1751px 1839px #ce93d8,\n    84px 775px #ce93d8,\n    1662px 1488px #ce93d8,\n    617px 1769px #ce93d8,\n    1869px 1292px #ce93d8,\n    963px 432px #ce93d8,\n    371px 1114px #ce93d8,\n    37px 642px #ce93d8,\n    21px 1184px #ce93d8,\n    602px 366px #ce93d8,\n    414px 524px #ce93d8,\n    282px 244px #ce93d8,\n    1689px 868px #ce93d8,\n    943px 681px #ce93d8,\n    898px 679px #ce93d8,\n    449px 1774px #ce93d8,\n    1678px 1313px #ce93d8,\n    475px 1811px #ce93d8,\n    1146px 1509px #ce93d8,\n    1151px 1863px #ce93d8,\n    1617px 846px #ce93d8,\n    82px 1077px #ce93d8,\n    324px 1317px #ce93d8,\n    1516px 885px #ce93d8,\n    1706px 1526px #ce93d8,\n    1925px 1180px #ce93d8,\n    553px 967px #ce93d8,\n    1072px 536px #ce93d8,\n    1715px 1816px #ce93d8,\n    185px 286px #ce93d8,\n    1362px 1600px #ce93d8,\n    628px 1938px #ce93d8,\n    1187px 412px #ce93d8,\n    569px 211px #ce93d8,\n    1959px 1356px #ce93d8,\n    1571px 105px #ce93d8,\n    319px 1111px #ce93d8,\n    36px 1364px #ce93d8,\n    502px 1788px #ce93d8,\n    1051px 1993px #ce93d8,\n    1617px 773px #ce93d8,\n    424px 1507px #ce93d8,\n    1623px 1955px #ce93d8,\n    307px 662px #ce93d8,\n    183px 1048px #ce93d8,\n    1919px 1453px #ce93d8,\n    1006px 1817px #ce93d8,\n    468px 673px #ce93d8,\n    1142px 1375px #ce93d8,\n    1228px 443px #ce93d8,\n    1734px 552px #ce93d8,\n    20px 1041px #ce93d8,\n    1783px 334px #ce93d8,\n    98px 1237px #ce93d8,\n    1356px 1940px #ce93d8,\n    853px 1779px #ce93d8,\n    1910px 560px #ce93d8,\n    1174px 1656px #ce93d8,\n    110px 1724px #ce93d8,\n    542px 1771px #ce93d8,\n    1758px 1931px #ce93d8,\n    1463px 1401px #ce93d8,\n    1155px 84px #ce93d8,\n    1504px 835px #ce93d8,\n    750px 322px #ce93d8,\n    407px 1900px #ce93d8,\n    1600px 1141px #ce93d8,\n    657px 886px #ce93d8,\n    526px 714px #ce93d8,\n    18px 836px #ce93d8,\n    1546px 1548px #ce93d8,\n    22px 469px #ce93d8,\n    594px 1466px #ce93d8,\n    1160px 1078px #ce93d8,\n    627px 1055px #ce93d8,\n    195px 699px #ce93d8,\n    1099px 684px #ce93d8,\n    530px 551px #ce93d8,\n    1160px 1325px #ce93d8,\n    894px 727px #ce93d8,\n    1157px 98px #ce93d8,\n    136px 1483px #ce93d8,\n    1875px 1975px #ce93d8,\n    1803px 566px #ce93d8,\n    318px 1073px #ce93d8,\n    1866px 1656px #ce93d8,\n    543px 414px #ce93d8,\n    719px 474px #ce93d8,\n    1115px 738px #ce93d8,\n    353px 875px #ce93d8,\n    184px 1938px #ce93d8,\n    1854px 1534px #ce93d8,\n    420px 1698px #ce93d8,\n    1480px 1550px #ce93d8,\n    522px 203px #ce93d8,\n    1897px 1904px #ce93d8,\n    975px 1708px #ce93d8,\n    1774px 602px #ce93d8,\n    1908px 274px #ce93d8,\n    61px 715px #ce93d8,\n    983px 1156px #ce93d8,\n    326px 1013px #ce93d8,\n    641px 290px #ce93d8,\n    1522px 120px #ce93d8,\n    405px 1637px #ce93d8,\n    1021px 1099px #ce93d8,\n    631px 1145px #ce93d8,\n    982px 1967px #ce93d8,\n    200px 651px #ce93d8,\n    795px 351px #ce93d8,\n    790px 1082px #ce93d8,\n    144px 1572px #ce93d8,\n    1542px 901px #ce93d8,\n    158px 1524px #ce93d8,\n    849px 1843px #ce93d8,\n    1807px 203px #ce93d8,\n    1747px 45px #ce93d8,\n    1603px 1738px #ce93d8,\n    617px 1966px #ce93d8,\n    342px 748px #ce93d8,\n    1779px 1173px #ce93d8,\n    1428px 152px #ce93d8,\n    589px 1998px #ce93d8,\n    1940px 1838px #ce93d8,\n    115px 272px #ce93d8,\n    1217px 1395px #ce93d8,\n    1402px 1491px #ce93d8,\n    1833px 1814px #ce93d8,\n    243px 966px #ce93d8,\n    319px 578px #ce93d8,\n    813px 364px #ce93d8,\n    669px 882px #ce93d8,\n    551px 134px #ce93d8,\n    1819px 920px #ce93d8,\n    740px 1826px #ce93d8,\n    1021px 952px #ce93d8,\n    1575px 453px #ce93d8,\n    324px 419px #ce93d8,\n    929px 417px #ce93d8,\n    885px 1112px #ce93d8,\n    503px 187px #ce93d8,\n    1908px 362px #ce93d8,\n    1063px 1601px #ce93d8,\n    169px 1792px #ce93d8,\n    789px 963px #ce93d8,\n    1697px 948px #ce93d8,\n    1761px 1810px #ce93d8,\n    1844px 1591px #ce93d8,\n    1709px 949px #ce93d8,\n    1402px 1396px #ce93d8,\n    1037px 225px #ce93d8,\n    1832px 518px #ce93d8,\n    1728px 1782px #ce93d8,\n    194px 1421px #ce93d8,\n    1395px 742px #ce93d8,\n    1478px 1325px #ce93d8,\n    40px 593px #ce93d8,\n    1732px 117px #ce93d8,\n    51px 158px #ce93d8,\n    1598px 1672px #ce93d8,\n    701px 849px #ce93d8,\n    1403px 1979px #ce93d8,\n    145px 1414px #ce93d8,\n    550px 906px #ce93d8,\n    1366px 460px #ce93d8,\n    142px 1379px #ce93d8,\n    34px 1864px #ce93d8,\n    1346px 308px #ce93d8,\n    293px 998px #ce93d8,\n    21px 1868px #ce93d8,\n    540px 1033px #ce93d8,\n    60px 746px #ce93d8,\n    1602px 1476px #ce93d8,\n    180px 804px #ce93d8,\n    345px 1982px #ce93d8,\n    1439px 640px #ce93d8,\n    939px 1834px #ce93d8,\n    20px 432px #ce93d8,\n    492px 1549px #ce93d8,\n    109px 1579px #ce93d8,\n    1796px 1403px #ce93d8,\n    1079px 519px #ce93d8,\n    1664px 389px #ce93d8,\n    1627px 1061px #ce93d8,\n    823px 419px #ce93d8,\n    1399px 1882px #ce93d8,\n    1906px 344px #ce93d8,\n    1189px 848px #ce93d8,\n    117px 882px #ce93d8,\n    1262px 33px #ce93d8,\n    1048px 434px #ce93d8,\n    1208px 1309px #ce93d8,\n    1616px 408px #ce93d8,\n    1833px 853px #ce93d8,\n    1433px 1656px #ce93d8,\n    811px 1861px #ce93d8,\n    439px 1672px #ce93d8,\n    1105px 248px #ce93d8,\n    328px 1652px #ce93d8,\n    13px 1658px #ce93d8,\n    685px 987px #ce93d8,\n    985px 403px #ce93d8,\n    1664px 1206px #ce93d8,\n    1993px 1925px #ce93d8,\n    440px 917px #ce93d8,\n    1835px 319px #ce93d8,\n    1404px 1907px #ce93d8,\n    624px 1443px #ce93d8,\n    843px 954px #ce93d8,\n    478px 1567px #ce93d8,\n    895px 1602px #ce93d8,\n    1231px 871px #ce93d8,\n    1267px 1646px #ce93d8,\n    475px 334px #ce93d8,\n    784px 796px #ce93d8,\n    1294px 199px #ce93d8,\n    109px 702px #ce93d8,\n    1978px 362px #ce93d8,\n    291px 940px #ce93d8,\n    971px 1343px #ce93d8,\n    74px 719px #ce93d8,\n    36px 715px #ce93d8,\n    1007px 1423px #ce93d8,\n    860px 314px #ce93d8,\n    631px 177px #ce93d8,\n    1900px 1590px #ce93d8,\n    1239px 1348px #ce93d8,\n    1346px 1270px #ce93d8,\n    1934px 1475px #ce93d8,\n    1553px 559px #ce93d8,\n    588px 1969px #ce93d8,\n    670px 1269px #ce93d8,\n    1484px 376px #ce93d8,\n    20px 1424px #ce93d8,\n    1396px 8px #ce93d8,\n    969px 244px #ce93d8,\n    1807px 538px #ce93d8,\n    1873px 891px #ce93d8,\n    636px 1142px #ce93d8,\n    1474px 1562px #ce93d8,\n    763px 350px #ce93d8,\n    663px 700px #ce93d8,\n    500px 1469px #ce93d8,\n    1302px 722px #ce93d8,\n    181px 291px #ce93d8,\n    266px 893px #ce93d8,\n    1403px 654px #ce93d8,\n    492px 460px #ce93d8,\n    1503px 1369px #ce93d8,\n    23px 1662px #ce93d8,\n    349px 333px #ce93d8,\n    1435px 1017px #ce93d8,\n    1441px 705px #ce93d8,\n    1708px 1446px #ce93d8,\n    1041px 911px #ce93d8,\n    1063px 780px #ce93d8,\n    1158px 1356px #ce93d8,\n    767px 1454px #ce93d8,\n    1912px 797px #ce93d8,\n    1731px 1759px #ce93d8,\n    1378px 1390px #ce93d8,\n    1815px 1364px #ce93d8,\n    960px 270px #ce93d8,\n    1343px 427px #ce93d8,\n    275px 203px #ce93d8,\n    1319px 1092px #ce93d8,\n    1455px 770px #ce93d8,\n    283px 1503px #ce93d8,\n    1505px 901px #ce93d8,\n    1738px 1561px #ce93d8,\n    1526px 1935px #ce93d8,\n    1757px 669px #ce93d8,\n    1640px 620px #ce93d8,\n    1750px 722px #ce93d8,\n    748px 66px #ce93d8,\n    1149px 540px #ce93d8,\n    159px 953px #ce93d8,\n    200px 1426px #ce93d8,\n    515px 1110px #ce93d8,\n    1552px 737px #ce93d8,\n    1094px 1459px #ce93d8,\n    778px 799px #ce93d8,\n    1031px 523px #ce93d8,\n    743px 1825px #ce93d8,\n    1100px 882px #ce93d8,\n    1088px 1836px #ce93d8,\n    255px 599px #ce93d8,\n    67px 1361px #ce93d8,\n    247px 1721px #ce93d8,\n    1722px 346px #ce93d8,\n    1822px 155px #ce93d8,\n    452px 1973px #ce93d8,\n    415px 1960px #ce93d8,\n    1109px 57px #ce93d8,\n    273px 1392px #ce93d8,\n    404px 1071px #ce93d8,\n    1212px 353px #ce93d8,\n    370px 460px #ce93d8,\n    795px 1523px #ce93d8,\n    1932px 340px #ce93d8,\n    51px 1473px #ce93d8,\n    1268px 364px #ce93d8,\n    1512px 1862px #ce93d8,\n    1678px 1801px #ce93d8,\n    1796px 579px #ce93d8,\n    254px 251px #ce93d8,\n    1466px 1717px #ce93d8,\n    893px 379px #ce93d8,\n    1153px 923px #ce93d8,\n    913px 1808px #ce93d8,\n    791px 789px #ce93d8,\n    417px 1924px #ce93d8,\n    1336px 1599px #ce93d8,\n    1695px 908px #ce93d8,\n    1120px 114px #ce93d8,\n    493px 1949px #ce93d8,\n    68px 1905px #ce93d8,\n    969px 481px #ce93d8,\n    1420px 1095px #ce93d8,\n    800px 1117px #ce93d8,\n    390px 234px #ce93d8,\n    356px 1644px #ce93d8,\n    1098px 1486px #ce93d8,\n    1360px 521px #ce93d8,\n    149px 1198px #ce93d8,\n    354px 747px #ce93d8,\n    1749px 487px #ce93d8,\n    470px 76px #ce93d8,\n    1672px 289px #ce93d8,\n    1731px 545px #ce93d8,\n    1547px 1590px #ce93d8,\n    498px 692px #ce93d8,\n    398px 1592px #ce93d8,\n    1846px 1237px #ce93d8,\n    1537px 1474px #ce93d8,\n    1726px 1374px #ce93d8,\n    1922px 858px #ce93d8,\n    376px 321px #ce93d8,\n    985px 227px #ce93d8,\n    234px 1421px #ce93d8,\n    760px 745px #ce93d8,\n    1990px 1132px #ce93d8,\n    1560px 1597px #ce93d8,\n    338px 1310px #ce93d8,\n    1924px 1664px #ce93d8,\n    547px 1747px #ce93d8,\n    1639px 1282px #ce93d8,\n    1202px 337px #ce93d8,\n    1985px 779px #ce93d8,\n    737px 456px #ce93d8,\n    89px 501px #ce93d8,\n    963px 792px #ce93d8,\n    655px 1447px #ce93d8,\n    1492px 1994px #ce93d8,\n    1171px 254px #ce93d8,\n    892px 827px #ce93d8,\n    1735px 442px #ce93d8,\n    1474px 1187px #ce93d8,\n    846px 1518px #ce93d8,\n    557px 1805px #ce93d8,\n    738px 945px #ce93d8,\n    795px 68px #ce93d8,\n    663px 1956px #ce93d8,\n    1607px 290px #ce93d8,\n    1524px 15px #ce93d8,\n    1097px 1911px #ce93d8,\n    157px 1939px #ce93d8,\n    935px 1065px #ce93d8,\n    1809px 1708px #ce93d8,\n    164px 1157px #ce93d8,\n    83px 855px #ce93d8,\n    625px 501px #ce93d8,\n    814px 398px #ce93d8,\n    552px 695px #ce93d8,\n    597px 1546px #ce93d8,\n    1237px 1417px #ce93d8,\n    628px 284px #ce93d8,\n    866px 767px #ce93d8,\n    1403px 1394px #ce93d8,\n    765px 1563px #ce93d8,\n    1648px 109px #ce93d8,\n    1205px 1659px #ce93d8,\n    921px 1313px #ce93d8,\n    1319px 243px #ce93d8,\n    18px 125px #ce93d8,\n    7px 777px #ce93d8,\n    181px 418px #ce93d8,\n    1062px 1892px #ce93d8,\n    382px 106px #ce93d8,\n    994px 751px #ce93d8,\n    964px 234px #ce93d8,\n    40px 118px #ce93d8,\n    278px 706px #ce93d8,\n    1540px 1978px #ce93d8,\n    425px 1661px #ce93d8,\n    1050px 321px #ce93d8,\n    735px 1729px #ce93d8,\n    1438px 260px #ce93d8,\n    1229px 1109px #ce93d8,\n    186px 1041px #ce93d8,\n    244px 1184px #ce93d8,\n    392px 1472px #ce93d8,\n    670px 1249px #ce93d8,\n    1260px 1443px #ce93d8,\n    1977px 1511px #ce93d8,\n    1240px 773px #ce93d8,\n    303px 513px #ce93d8,\n    63px 1530px #ce93d8,\n    610px 792px #ce93d8,\n    1987px 1647px #ce93d8,\n    676px 1597px #ce93d8,\n    1740px 1244px #ce93d8,\n    816px 1661px #ce93d8,\n    351px 802px #ce93d8,\n    252px 1082px #ce93d8,\n    31px 365px #ce93d8,\n    1453px 984px #ce93d8,\n    667px 1233px #ce93d8,\n    1247px 1800px #ce93d8,\n    839px 270px #ce93d8,\n    775px 913px #ce93d8,\n    1966px 1398px #ce93d8,\n    499px 813px #ce93d8,\n    922px 1982px #ce93d8,\n    1409px 1902px #ce93d8,\n    1499px 1766px #ce93d8,\n    721px 899px #ce93d8,\n    788px 807px #ce93d8,\n    989px 1355px #ce93d8,\n    1248px 1274px #ce93d8,\n    849px 1091px #ce93d8,\n    1799px 1036px #ce93d8,\n    1486px 700px #ce93d8,\n    170px 1989px #ce93d8,\n    1275px 799px #ce93d8,\n    772px 2000px #ce93d8,\n    1642px 362px #ce93d8,\n    216px 940px #ce93d8,\n    1893px 281px #ce93d8,\n    1944px 1298px #ce93d8,\n    1294px 400px #ce93d8,\n    1523px 441px #ce93d8,\n    1829px 340px #ce93d8,\n    468px 170px #ce93d8,\n    1099px 967px #ce93d8,\n    1331px 665px #ce93d8,\n    1174px 1553px #ce93d8,\n    1567px 325px #ce93d8,\n    1028px 1399px #ce93d8,\n    781px 1451px #ce93d8,\n    1912px 1954px #ce93d8,\n    874px 873px #ce93d8,\n    1298px 1722px #ce93d8,\n    1879px 706px #ce93d8,\n    57px 1221px #ce93d8,\n    1116px 1432px #ce93d8,\n    48px 811px #ce93d8,\n    101px 916px #ce93d8,\n    677px 304px #ce93d8,\n    1203px 639px #ce93d8,\n    1391px 199px #ce93d8,\n    1895px 1988px #ce93d8,\n    1462px 1023px #ce93d8,\n    1216px 1751px #ce93d8,\n    1261px 663px #ce93d8,\n    1290px 1119px #ce93d8,\n    137px 1793px #ce93d8,\n    1052px 1470px #ce93d8,\n    1561px 226px #ce93d8,\n    1156px 402px #ce93d8,\n    709px 693px #ce93d8,\n    1040px 1911px #ce93d8,\n    1624px 1115px #ce93d8,\n    551px 475px #ce93d8,\n    416px 1090px #ce93d8,\n    1183px 451px #ce93d8,\n    58px 765px #ce93d8,\n    743px 1016px #ce93d8,\n    198px 369px #ce93d8,\n    1645px 1503px #ce93d8,\n    997px 22px #ce93d8,\n    1447px 1323px #ce93d8,\n    379px 883px #ce93d8,\n    1171px 1195px #ce93d8,\n    919px 133px #ce93d8,\n    1400px 517px #ce93d8,\n    725px 804px #ce93d8,\n    1600px 699px #ce93d8,\n    357px 581px #ce93d8,\n    266px 1713px #ce93d8,\n    848px 1749px #ce93d8,\n    1963px 1045px #ce93d8,\n    119px 1136px #ce93d8;\n  animation: animStar 50s linear infinite;\n}\n\n#stars:after {\n  content: \" \";\n  position: absolute;\n  top: 2000px;\n  width: 1px;\n  height: 1px;\n  background: transparent;\n  box-shadow:\n    117px 1613px #ce93d8,\n    1488px 635px #ce93d8,\n    944px 914px #ce93d8,\n    647px 277px #ce93d8,\n    1792px 1205px #ce93d8,\n    656px 1517px #ce93d8,\n    820px 1839px #ce93d8,\n    1153px 1400px #ce93d8,\n    870px 13px #ce93d8,\n    550px 702px #ce93d8,\n    1155px 1056px #ce93d8,\n    88px 1709px #ce93d8,\n    1450px 1090px #ce93d8,\n    1929px 457px #ce93d8,\n    1390px 905px #ce93d8,\n    1771px 269px #ce93d8,\n    1741px 669px #ce93d8,\n    432px 64px #ce93d8,\n    563px 996px #ce93d8,\n    1918px 1873px #ce93d8,\n    1845px 1211px #ce93d8,\n    231px 1503px #ce93d8,\n    37px 220px #ce93d8,\n    1970px 495px #ce93d8,\n    1812px 925px #ce93d8,\n    67px 1398px #ce93d8,\n    535px 279px #ce93d8,\n    1837px 829px #ce93d8,\n    1945px 685px #ce93d8,\n    1677px 1817px #ce93d8,\n    1317px 1415px #ce93d8,\n    1785px 905px #ce93d8,\n    1787px 1554px #ce93d8,\n    802px 1296px #ce93d8,\n    512px 1101px #ce93d8,\n    583px 1364px #ce93d8,\n    336px 558px #ce93d8,\n    979px 334px #ce93d8,\n    106px 792px #ce93d8,\n    204px 34px #ce93d8,\n    1845px 1763px #ce93d8,\n    445px 1599px #ce93d8,\n    386px 453px #ce93d8,\n    471px 952px #ce93d8,\n    1466px 1676px #ce93d8,\n    1885px 303px #ce93d8,\n    51px 1717px #ce93d8,\n    1211px 299px #ce93d8,\n    1546px 1887px #ce93d8,\n    1067px 33px #ce93d8,\n    1088px 1326px #ce93d8,\n    1938px 760px #ce93d8,\n    470px 648px #ce93d8,\n    1213px 269px #ce93d8,\n    1767px 78px #ce93d8,\n    977px 976px #ce93d8,\n    1926px 175px #ce93d8,\n    722px 1512px #ce93d8,\n    945px 227px #ce93d8,\n    1811px 99px #ce93d8,\n    1912px 1406px #ce93d8,\n    1602px 1243px #ce93d8,\n    610px 449px #ce93d8,\n    654px 1393px #ce93d8,\n    1930px 1193px #ce93d8,\n    258px 1184px #ce93d8,\n    89px 265px #ce93d8,\n    824px 1494px #ce93d8,\n    1506px 1435px #ce93d8,\n    1027px 753px #ce93d8,\n    1px 1197px #ce93d8,\n    530px 1161px #ce93d8,\n    864px 1555px #ce93d8,\n    1610px 1604px #ce93d8,\n    1035px 1114px #ce93d8,\n    1456px 133px #ce93d8,\n    1196px 1253px #ce93d8,\n    361px 1037px #ce93d8,\n    834px 351px #ce93d8,\n    436px 1676px #ce93d8,\n    1194px 1007px #ce93d8,\n    1141px 647px #ce93d8,\n    319px 454px #ce93d8,\n    937px 1769px #ce93d8,\n    1872px 1013px #ce93d8,\n    733px 643px #ce93d8,\n    1250px 511px #ce93d8,\n    189px 296px #ce93d8,\n    1639px 163px #ce93d8,\n    1584px 336px #ce93d8,\n    1912px 1343px #ce93d8,\n    1298px 1307px #ce93d8,\n    1750px 902px #ce93d8,\n    1129px 845px #ce93d8,\n    1899px 1470px #ce93d8,\n    1427px 232px #ce93d8,\n    1391px 838px #ce93d8,\n    1225px 1819px #ce93d8,\n    190px 1366px #ce93d8,\n    1865px 518px #ce93d8,\n    203px 1383px #ce93d8,\n    1455px 614px #ce93d8,\n    423px 354px #ce93d8,\n    1678px 1790px #ce93d8,\n    241px 608px #ce93d8,\n    1089px 730px #ce93d8,\n    1342px 38px #ce93d8,\n    1848px 249px #ce93d8,\n    1874px 1785px #ce93d8,\n    1040px 1837px #ce93d8,\n    751px 261px #ce93d8,\n    510px 1975px #ce93d8,\n    52px 795px #ce93d8,\n    1786px 1310px #ce93d8,\n    498px 712px #ce93d8,\n    190px 375px #ce93d8,\n    1341px 722px #ce93d8,\n    43px 1394px #ce93d8,\n    1821px 1687px #ce93d8,\n    106px 130px #ce93d8,\n    1717px 1978px #ce93d8,\n    168px 151px #ce93d8,\n    183px 740px #ce93d8,\n    945px 1381px #ce93d8,\n    669px 1170px #ce93d8,\n    1285px 1816px #ce93d8,\n    110px 1217px #ce93d8,\n    1623px 813px #ce93d8,\n    869px 647px #ce93d8,\n    867px 582px #ce93d8,\n    735px 1240px #ce93d8,\n    519px 1896px #ce93d8,\n    132px 156px #ce93d8,\n    1649px 193px #ce93d8,\n    241px 1109px #ce93d8,\n    643px 484px #ce93d8,\n    574px 1282px #ce93d8,\n    1952px 564px #ce93d8,\n    1978px 145px #ce93d8,\n    329px 903px #ce93d8,\n    1674px 617px #ce93d8,\n    1978px 558px #ce93d8,\n    1808px 1715px #ce93d8,\n    1526px 1238px #ce93d8,\n    475px 1330px #ce93d8,\n    810px 425px #ce93d8,\n    1709px 634px #ce93d8,\n    1658px 336px #ce93d8,\n    425px 194px #ce93d8,\n    352px 96px #ce93d8,\n    148px 180px #ce93d8,\n    1139px 1046px #ce93d8,\n    1809px 1233px #ce93d8,\n    1669px 171px #ce93d8,\n    263px 1394px #ce93d8,\n    534px 715px #ce93d8,\n    396px 1008px #ce93d8,\n    589px 1445px #ce93d8,\n    1190px 381px #ce93d8,\n    1709px 279px #ce93d8,\n    520px 891px #ce93d8,\n    1136px 1867px #ce93d8,\n    1280px 1233px #ce93d8,\n    836px 296px #ce93d8,\n    1348px 646px #ce93d8,\n    1539px 913px #ce93d8,\n    423px 781px #ce93d8,\n    1271px 1805px #ce93d8,\n    696px 564px #ce93d8,\n    1549px 804px #ce93d8,\n    303px 1555px #ce93d8,\n    1449px 1903px #ce93d8,\n    66px 687px #ce93d8,\n    1164px 856px #ce93d8,\n    1958px 1326px #ce93d8,\n    125px 157px #ce93d8,\n    508px 1669px #ce93d8,\n    465px 725px #ce93d8,\n    1925px 1440px #ce93d8,\n    405px 793px #ce93d8,\n    278px 110px #ce93d8,\n    1084px 1065px #ce93d8,\n    1077px 705px #ce93d8,\n    663px 1844px #ce93d8,\n    734px 263px #ce93d8,\n    870px 1761px #ce93d8,\n    103px 1169px #ce93d8,\n    1506px 1295px #ce93d8,\n    1883px 926px #ce93d8,\n    335px 1361px #ce93d8,\n    1126px 1284px #ce93d8,\n    257px 1165px #ce93d8,\n    837px 580px #ce93d8,\n    1211px 1362px #ce93d8,\n    1137px 1380px #ce93d8,\n    135px 632px #ce93d8,\n    1491px 1965px #ce93d8,\n    1098px 195px #ce93d8,\n    506px 417px #ce93d8,\n    693px 1243px #ce93d8,\n    622px 1862px #ce93d8,\n    1412px 1343px #ce93d8,\n    948px 1894px #ce93d8,\n    1315px 1363px #ce93d8,\n    754px 1098px #ce93d8,\n    1931px 930px #ce93d8,\n    1831px 342px #ce93d8,\n    1751px 1839px #ce93d8,\n    84px 775px #ce93d8,\n    1662px 1488px #ce93d8,\n    617px 1769px #ce93d8,\n    1869px 1292px #ce93d8,\n    963px 432px #ce93d8,\n    371px 1114px #ce93d8,\n    37px 642px #ce93d8,\n    21px 1184px #ce93d8,\n    602px 366px #ce93d8,\n    414px 524px #ce93d8,\n    282px 244px #ce93d8,\n    1689px 868px #ce93d8,\n    943px 681px #ce93d8,\n    898px 679px #ce93d8,\n    449px 1774px #ce93d8,\n    1678px 1313px #ce93d8,\n    475px 1811px #ce93d8,\n    1146px 1509px #ce93d8,\n    1151px 1863px #ce93d8,\n    1617px 846px #ce93d8,\n    82px 1077px #ce93d8,\n    324px 1317px #ce93d8,\n    1516px 885px #ce93d8,\n    1706px 1526px #ce93d8,\n    1925px 1180px #ce93d8,\n    553px 967px #ce93d8,\n    1072px 536px #ce93d8,\n    1715px 1816px #ce93d8,\n    185px 286px #ce93d8,\n    1362px 1600px #ce93d8,\n    628px 1938px #ce93d8,\n    1187px 412px #ce93d8,\n    569px 211px #ce93d8,\n    1959px 1356px #ce93d8,\n    1571px 105px #ce93d8,\n    319px 1111px #ce93d8,\n    36px 1364px #ce93d8,\n    502px 1788px #ce93d8,\n    1051px 1993px #ce93d8,\n    1617px 773px #ce93d8,\n    424px 1507px #ce93d8,\n    1623px 1955px #ce93d8,\n    307px 662px #ce93d8,\n    183px 1048px #ce93d8,\n    1919px 1453px #ce93d8,\n    1006px 1817px #ce93d8,\n    468px 673px #ce93d8,\n    1142px 1375px #ce93d8,\n    1228px 443px #ce93d8,\n    1734px 552px #ce93d8,\n    20px 1041px #ce93d8,\n    1783px 334px #ce93d8,\n    98px 1237px #ce93d8,\n    1356px 1940px #ce93d8,\n    853px 1779px #ce93d8,\n    1910px 560px #ce93d8,\n    1174px 1656px #ce93d8,\n    110px 1724px #ce93d8,\n    542px 1771px #ce93d8,\n    1758px 1931px #ce93d8,\n    1463px 1401px #ce93d8,\n    1155px 84px #ce93d8,\n    1504px 835px #ce93d8,\n    750px 322px #ce93d8,\n    407px 1900px #ce93d8,\n    1600px 1141px #ce93d8,\n    657px 886px #ce93d8,\n    526px 714px #ce93d8,\n    18px 836px #ce93d8,\n    1546px 1548px #ce93d8,\n    22px 469px #ce93d8,\n    594px 1466px #ce93d8,\n    1160px 1078px #ce93d8,\n    627px 1055px #ce93d8,\n    195px 699px #ce93d8,\n    1099px 684px #ce93d8,\n    530px 551px #ce93d8,\n    1160px 1325px #ce93d8,\n    894px 727px #ce93d8,\n    1157px 98px #ce93d8,\n    136px 1483px #ce93d8,\n    1875px 1975px #ce93d8,\n    1803px 566px #ce93d8,\n    318px 1073px #ce93d8,\n    1866px 1656px #ce93d8,\n    543px 414px #ce93d8,\n    719px 474px #ce93d8,\n    1115px 738px #ce93d8,\n    353px 875px #ce93d8,\n    184px 1938px #ce93d8,\n    1854px 1534px #ce93d8,\n    420px 1698px #ce93d8,\n    1480px 1550px #ce93d8,\n    522px 203px #ce93d8,\n    1897px 1904px #ce93d8,\n    975px 1708px #ce93d8,\n    1774px 602px #ce93d8,\n    1908px 274px #ce93d8,\n    61px 715px #ce93d8,\n    983px 1156px #ce93d8,\n    326px 1013px #ce93d8,\n    641px 290px #ce93d8,\n    1522px 120px #ce93d8,\n    405px 1637px #ce93d8,\n    1021px 1099px #ce93d8,\n    631px 1145px #ce93d8,\n    982px 1967px #ce93d8,\n    200px 651px #ce93d8,\n    795px 351px #ce93d8,\n    790px 1082px #ce93d8,\n    144px 1572px #ce93d8,\n    1542px 901px #ce93d8,\n    158px 1524px #ce93d8,\n    849px 1843px #ce93d8,\n    1807px 203px #ce93d8,\n    1747px 45px #ce93d8,\n    1603px 1738px #ce93d8,\n    617px 1966px #ce93d8,\n    342px 748px #ce93d8,\n    1779px 1173px #ce93d8,\n    1428px 152px #ce93d8,\n    589px 1998px #ce93d8,\n    1940px 1838px #ce93d8,\n    115px 272px #ce93d8,\n    1217px 1395px #ce93d8,\n    1402px 1491px #ce93d8,\n    1833px 1814px #ce93d8,\n    243px 966px #ce93d8,\n    319px 578px #ce93d8,\n    813px 364px #ce93d8,\n    669px 882px #ce93d8,\n    551px 134px #ce93d8,\n    1819px 920px #ce93d8,\n    740px 1826px #ce93d8,\n    1021px 952px #ce93d8,\n    1575px 453px #ce93d8,\n    324px 419px #ce93d8,\n    929px 417px #ce93d8,\n    885px 1112px #ce93d8,\n    503px 187px #ce93d8,\n    1908px 362px #ce93d8,\n    1063px 1601px #ce93d8,\n    169px 1792px #ce93d8,\n    789px 963px #ce93d8,\n    1697px 948px #ce93d8,\n    1761px 1810px #ce93d8,\n    1844px 1591px #ce93d8,\n    1709px 949px #ce93d8,\n    1402px 1396px #ce93d8,\n    1037px 225px #ce93d8,\n    1832px 518px #ce93d8,\n    1728px 1782px #ce93d8,\n    194px 1421px #ce93d8,\n    1395px 742px #ce93d8,\n    1478px 1325px #ce93d8,\n    40px 593px #ce93d8,\n    1732px 117px #ce93d8,\n    51px 158px #ce93d8,\n    1598px 1672px #ce93d8,\n    701px 849px #ce93d8,\n    1403px 1979px #ce93d8,\n    145px 1414px #ce93d8,\n    550px 906px #ce93d8,\n    1366px 460px #ce93d8,\n    142px 1379px #ce93d8,\n    34px 1864px #ce93d8,\n    1346px 308px #ce93d8,\n    293px 998px #ce93d8,\n    21px 1868px #ce93d8,\n    540px 1033px #ce93d8,\n    60px 746px #ce93d8,\n    1602px 1476px #ce93d8,\n    180px 804px #ce93d8,\n    345px 1982px #ce93d8,\n    1439px 640px #ce93d8,\n    939px 1834px #ce93d8,\n    20px 432px #ce93d8,\n    492px 1549px #ce93d8,\n    109px 1579px #ce93d8,\n    1796px 1403px #ce93d8,\n    1079px 519px #ce93d8,\n    1664px 389px #ce93d8,\n    1627px 1061px #ce93d8,\n    823px 419px #ce93d8,\n    1399px 1882px #ce93d8,\n    1906px 344px #ce93d8,\n    1189px 848px #ce93d8,\n    117px 882px #ce93d8,\n    1262px 33px #ce93d8,\n    1048px 434px #ce93d8,\n    1208px 1309px #ce93d8,\n    1616px 408px #ce93d8,\n    1833px 853px #ce93d8,\n    1433px 1656px #ce93d8,\n    811px 1861px #ce93d8,\n    439px 1672px #ce93d8,\n    1105px 248px #ce93d8,\n    328px 1652px #ce93d8,\n    13px 1658px #ce93d8,\n    685px 987px #ce93d8,\n    985px 403px #ce93d8,\n    1664px 1206px #ce93d8,\n    1993px 1925px #ce93d8,\n    440px 917px #ce93d8,\n    1835px 319px #ce93d8,\n    1404px 1907px #ce93d8,\n    624px 1443px #ce93d8,\n    843px 954px #ce93d8,\n    478px 1567px #ce93d8,\n    895px 1602px #ce93d8,\n    1231px 871px #ce93d8,\n    1267px 1646px #ce93d8,\n    475px 334px #ce93d8,\n    784px 796px #ce93d8,\n    1294px 199px #ce93d8,\n    109px 702px #ce93d8,\n    1978px 362px #ce93d8,\n    291px 940px #ce93d8,\n    971px 1343px #ce93d8,\n    74px 719px #ce93d8,\n    36px 715px #ce93d8,\n    1007px 1423px #ce93d8,\n    860px 314px #ce93d8,\n    631px 177px #ce93d8,\n    1900px 1590px #ce93d8,\n    1239px 1348px #ce93d8,\n    1346px 1270px #ce93d8,\n    1934px 1475px #ce93d8,\n    1553px 559px #ce93d8,\n    588px 1969px #ce93d8,\n    670px 1269px #ce93d8,\n    1484px 376px #ce93d8,\n    20px 1424px #ce93d8,\n    1396px 8px #ce93d8,\n    969px 244px #ce93d8,\n    1807px 538px #ce93d8,\n    1873px 891px #ce93d8,\n    636px 1142px #ce93d8,\n    1474px 1562px #ce93d8,\n    763px 350px #ce93d8,\n    663px 700px #ce93d8,\n    500px 1469px #ce93d8,\n    1302px 722px #ce93d8,\n    181px 291px #ce93d8,\n    266px 893px #ce93d8,\n    1403px 654px #ce93d8,\n    492px 460px #ce93d8,\n    1503px 1369px #ce93d8,\n    23px 1662px #ce93d8,\n    349px 333px #ce93d8,\n    1435px 1017px #ce93d8,\n    1441px 705px #ce93d8,\n    1708px 1446px #ce93d8,\n    1041px 911px #ce93d8,\n    1063px 780px #ce93d8,\n    1158px 1356px #ce93d8,\n    767px 1454px #ce93d8,\n    1912px 797px #ce93d8,\n    1731px 1759px #ce93d8,\n    1378px 1390px #ce93d8,\n    1815px 1364px #ce93d8,\n    960px 270px #ce93d8,\n    1343px 427px #ce93d8,\n    275px 203px #ce93d8,\n    1319px 1092px #ce93d8,\n    1455px 770px #ce93d8,\n    283px 1503px #ce93d8,\n    1505px 901px #ce93d8,\n    1738px 1561px #ce93d8,\n    1526px 1935px #ce93d8,\n    1757px 669px #ce93d8,\n    1640px 620px #ce93d8,\n    1750px 722px #ce93d8,\n    748px 66px #ce93d8,\n    1149px 540px #ce93d8,\n    159px 953px #ce93d8,\n    200px 1426px #ce93d8,\n    515px 1110px #ce93d8,\n    1552px 737px #ce93d8,\n    1094px 1459px #ce93d8,\n    778px 799px #ce93d8,\n    1031px 523px #ce93d8,\n    743px 1825px #ce93d8,\n    1100px 882px #ce93d8,\n    1088px 1836px #ce93d8,\n    255px 599px #ce93d8,\n    67px 1361px #ce93d8,\n    247px 1721px #ce93d8,\n    1722px 346px #ce93d8,\n    1822px 155px #ce93d8,\n    452px 1973px #ce93d8,\n    415px 1960px #ce93d8,\n    1109px 57px #ce93d8,\n    273px 1392px #ce93d8,\n    404px 1071px #ce93d8,\n    1212px 353px #ce93d8,\n    370px 460px #ce93d8,\n    795px 1523px #ce93d8,\n    1932px 340px #ce93d8,\n    51px 1473px #ce93d8,\n    1268px 364px #ce93d8,\n    1512px 1862px #ce93d8,\n    1678px 1801px #ce93d8,\n    1796px 579px #ce93d8,\n    254px 251px #ce93d8,\n    1466px 1717px #ce93d8,\n    893px 379px #ce93d8,\n    1153px 923px #ce93d8,\n    913px 1808px #ce93d8,\n    791px 789px #ce93d8,\n    417px 1924px #ce93d8,\n    1336px 1599px #ce93d8,\n    1695px 908px #ce93d8,\n    1120px 114px #ce93d8,\n    493px 1949px #ce93d8,\n    68px 1905px #ce93d8,\n    969px 481px #ce93d8,\n    1420px 1095px #ce93d8,\n    800px 1117px #ce93d8,\n    390px 234px #ce93d8,\n    356px 1644px #ce93d8,\n    1098px 1486px #ce93d8,\n    1360px 521px #ce93d8,\n    149px 1198px #ce93d8,\n    354px 747px #ce93d8,\n    1749px 487px #ce93d8,\n    470px 76px #ce93d8,\n    1672px 289px #ce93d8,\n    1731px 545px #ce93d8,\n    1547px 1590px #ce93d8,\n    498px 692px #ce93d8,\n    398px 1592px #ce93d8,\n    1846px 1237px #ce93d8,\n    1537px 1474px #ce93d8,\n    1726px 1374px #ce93d8,\n    1922px 858px #ce93d8,\n    376px 321px #ce93d8,\n    985px 227px #ce93d8,\n    234px 1421px #ce93d8,\n    760px 745px #ce93d8,\n    1990px 1132px #ce93d8,\n    1560px 1597px #ce93d8,\n    338px 1310px #ce93d8,\n    1924px 1664px #ce93d8,\n    547px 1747px #ce93d8,\n    1639px 1282px #ce93d8,\n    1202px 337px #ce93d8,\n    1985px 779px #ce93d8,\n    737px 456px #ce93d8,\n    89px 501px #ce93d8,\n    963px 792px #ce93d8,\n    655px 1447px #ce93d8,\n    1492px 1994px #ce93d8,\n    1171px 254px #ce93d8,\n    892px 827px #ce93d8,\n    1735px 442px #ce93d8,\n    1474px 1187px #ce93d8,\n    846px 1518px #ce93d8,\n    557px 1805px #ce93d8,\n    738px 945px #ce93d8,\n    795px 68px #ce93d8,\n    663px 1956px #ce93d8,\n    1607px 290px #ce93d8,\n    1524px 15px #ce93d8,\n    1097px 1911px #ce93d8,\n    157px 1939px #ce93d8,\n    935px 1065px #ce93d8,\n    1809px 1708px #ce93d8,\n    164px 1157px #ce93d8,\n    83px 855px #ce93d8,\n    625px 501px #ce93d8,\n    814px 398px #ce93d8,\n    552px 695px #ce93d8,\n    597px 1546px #ce93d8,\n    1237px 1417px #ce93d8,\n    628px 284px #ce93d8,\n    866px 767px #ce93d8,\n    1403px 1394px #ce93d8,\n    765px 1563px #ce93d8,\n    1648px 109px #ce93d8,\n    1205px 1659px #ce93d8,\n    921px 1313px #ce93d8,\n    1319px 243px #ce93d8,\n    18px 125px #ce93d8,\n    7px 777px #ce93d8,\n    181px 418px #ce93d8,\n    1062px 1892px #ce93d8,\n    382px 106px #ce93d8,\n    994px 751px #ce93d8,\n    964px 234px #ce93d8,\n    40px 118px #ce93d8,\n    278px 706px #ce93d8,\n    1540px 1978px #ce93d8,\n    425px 1661px #ce93d8,\n    1050px 321px #ce93d8,\n    735px 1729px #ce93d8,\n    1438px 260px #ce93d8,\n    1229px 1109px #ce93d8,\n    186px 1041px #ce93d8,\n    244px 1184px #ce93d8,\n    392px 1472px #ce93d8,\n    670px 1249px #ce93d8,\n    1260px 1443px #ce93d8,\n    1977px 1511px #ce93d8,\n    1240px 773px #ce93d8,\n    303px 513px #ce93d8,\n    63px 1530px #ce93d8,\n    610px 792px #ce93d8,\n    1987px 1647px #ce93d8,\n    676px 1597px #ce93d8,\n    1740px 1244px #ce93d8,\n    816px 1661px #ce93d8,\n    351px 802px #ce93d8,\n    252px 1082px #ce93d8,\n    31px 365px #ce93d8,\n    1453px 984px #ce93d8,\n    667px 1233px #ce93d8,\n    1247px 1800px #ce93d8,\n    839px 270px #ce93d8,\n    775px 913px #ce93d8,\n    1966px 1398px #ce93d8,\n    499px 813px #ce93d8,\n    922px 1982px #ce93d8,\n    1409px 1902px #ce93d8,\n    1499px 1766px #ce93d8,\n    721px 899px #ce93d8,\n    788px 807px #ce93d8,\n    989px 1355px #ce93d8,\n    1248px 1274px #ce93d8,\n    849px 1091px #ce93d8,\n    1799px 1036px #ce93d8,\n    1486px 700px #ce93d8,\n    170px 1989px #ce93d8,\n    1275px 799px #ce93d8,\n    772px 2000px #ce93d8,\n    1642px 362px #ce93d8,\n    216px 940px #ce93d8,\n    1893px 281px #ce93d8,\n    1944px 1298px #ce93d8,\n    1294px 400px #ce93d8,\n    1523px 441px #ce93d8,\n    1829px 340px #ce93d8,\n    468px 170px #ce93d8,\n    1099px 967px #ce93d8,\n    1331px 665px #ce93d8,\n    1174px 1553px #ce93d8,\n    1567px 325px #ce93d8,\n    1028px 1399px #ce93d8,\n    781px 1451px #ce93d8,\n    1912px 1954px #ce93d8,\n    874px 873px #ce93d8,\n    1298px 1722px #ce93d8,\n    1879px 706px #ce93d8,\n    57px 1221px #ce93d8,\n    1116px 1432px #ce93d8,\n    48px 811px #ce93d8,\n    101px 916px #ce93d8,\n    677px 304px #ce93d8,\n    1203px 639px #ce93d8,\n    1391px 199px #ce93d8,\n    1895px 1988px #ce93d8,\n    1462px 1023px #ce93d8,\n    1216px 1751px #ce93d8,\n    1261px 663px #ce93d8,\n    1290px 1119px #ce93d8,\n    137px 1793px #ce93d8,\n    1052px 1470px #ce93d8,\n    1561px 226px #ce93d8,\n    1156px 402px #ce93d8,\n    709px 693px #ce93d8,\n    1040px 1911px #ce93d8,\n    1624px 1115px #ce93d8,\n    551px 475px #ce93d8,\n    416px 1090px #ce93d8,\n    1183px 451px #ce93d8,\n    58px 765px #ce93d8,\n    743px 1016px #ce93d8,\n    198px 369px #ce93d8,\n    1645px 1503px #ce93d8,\n    997px 22px #ce93d8,\n    1447px 1323px #ce93d8,\n    379px 883px #ce93d8,\n    1171px 1195px #ce93d8,\n    919px 133px #ce93d8,\n    1400px 517px #ce93d8,\n    725px 804px #ce93d8,\n    1600px 699px #ce93d8,\n    357px 581px #ce93d8,\n    266px 1713px #ce93d8,\n    848px 1749px #ce93d8,\n    1963px 1045px #ce93d8,\n    119px 1136px #ce93d8;\n}\n\n#stars2 {\n  width: 2px;\n  height: 2px;\n  background: transparent;\n  box-shadow:\n    1117px 1306px #ce93d8,\n    1078px 1783px #ce93d8,\n    1179px 1085px #ce93d8,\n    1145px 920px #ce93d8,\n    422px 1233px #ce93d8,\n    387px 98px #ce93d8,\n    1153px 637px #ce93d8,\n    1084px 782px #ce93d8,\n    476px 453px #ce93d8,\n    926px 1306px #ce93d8,\n    60px 1086px #ce93d8,\n    753px 1575px #ce93d8,\n    272px 1684px #ce93d8,\n    1285px 750px #ce93d8,\n    1416px 1327px #ce93d8,\n    1931px 473px #ce93d8,\n    736px 1395px #ce93d8,\n    1816px 763px #ce93d8,\n    438px 879px #ce93d8,\n    665px 1902px #ce93d8,\n    1341px 677px #ce93d8,\n    1404px 1073px #ce93d8,\n    100px 597px #ce93d8,\n    357px 1689px #ce93d8,\n    1044px 1342px #ce93d8,\n    1954px 502px #ce93d8,\n    1192px 1308px #ce93d8,\n    540px 1239px #ce93d8,\n    1360px 552px #ce93d8,\n    89px 752px #ce93d8,\n    659px 1253px #ce93d8,\n    62px 517px #ce93d8,\n    1375px 1705px #ce93d8,\n    1343px 1511px #ce93d8,\n    1659px 1922px #ce93d8,\n    1560px 289px #ce93d8,\n    1362px 1799px #ce93d8,\n    1886px 1480px #ce93d8,\n    1718px 1885px #ce93d8,\n    824px 738px #ce93d8,\n    1060px 1370px #ce93d8,\n    1781px 1171px #ce93d8,\n    255px 273px #ce93d8,\n    1197px 120px #ce93d8,\n    213px 7px #ce93d8,\n    1226px 1920px #ce93d8,\n    1844px 207px #ce93d8,\n    1675px 970px #ce93d8,\n    1435px 1283px #ce93d8,\n    37px 353px #ce93d8,\n    59px 417px #ce93d8,\n    921px 1602px #ce93d8,\n    1549px 1490px #ce93d8,\n    638px 1845px #ce93d8,\n    1328px 198px #ce93d8,\n    1050px 1149px #ce93d8,\n    1884px 711px #ce93d8,\n    333px 263px #ce93d8,\n    342px 1508px #ce93d8,\n    1388px 1810px #ce93d8,\n    1377px 1558px #ce93d8,\n    890px 487px #ce93d8,\n    1081px 759px #ce93d8,\n    890px 1515px #ce93d8,\n    911px 1284px #ce93d8,\n    335px 735px #ce93d8,\n    1140px 549px #ce93d8,\n    1239px 1064px #ce93d8,\n    226px 71px #ce93d8,\n    1100px 1278px #ce93d8,\n    1851px 1805px #ce93d8,\n    1370px 1999px #ce93d8,\n    1008px 1122px #ce93d8,\n    785px 813px #ce93d8,\n    1358px 601px #ce93d8,\n    1833px 1305px #ce93d8,\n    1768px 1304px #ce93d8,\n    1303px 532px #ce93d8,\n    860px 598px #ce93d8,\n    1329px 593px #ce93d8,\n    1038px 1088px #ce93d8,\n    408px 405px #ce93d8,\n    965px 82px #ce93d8,\n    1483px 1438px #ce93d8,\n    310px 1479px #ce93d8,\n    1786px 1500px #ce93d8,\n    1866px 852px #ce93d8,\n    18px 1757px #ce93d8,\n    1473px 1004px #ce93d8,\n    1542px 1933px #ce93d8,\n    633px 1970px #ce93d8,\n    1334px 1713px #ce93d8,\n    175px 28px #ce93d8,\n    592px 894px #ce93d8,\n    121px 1162px #ce93d8,\n    1601px 1567px #ce93d8,\n    1095px 657px #ce93d8,\n    640px 1233px #ce93d8,\n    1073px 1255px #ce93d8,\n    840px 1087px #ce93d8,\n    718px 250px #ce93d8,\n    967px 709px #ce93d8,\n    731px 239px #ce93d8,\n    1623px 593px #ce93d8,\n    1058px 1820px #ce93d8,\n    516px 1898px #ce93d8,\n    666px 12px #ce93d8,\n    1997px 1382px #ce93d8,\n    112px 1690px #ce93d8,\n    687px 1309px #ce93d8,\n    63px 539px #ce93d8,\n    185px 1897px #ce93d8,\n    1055px 1691px #ce93d8,\n    435px 1517px #ce93d8,\n    1175px 1119px #ce93d8,\n    1721px 133px #ce93d8,\n    1212px 47px #ce93d8,\n    166px 18px #ce93d8,\n    1416px 1652px #ce93d8,\n    1409px 1745px #ce93d8,\n    1357px 1232px #ce93d8,\n    1677px 1998px #ce93d8,\n    448px 1415px #ce93d8,\n    705px 1736px #ce93d8,\n    1031px 1466px #ce93d8,\n    543px 1651px #ce93d8,\n    1592px 1888px #ce93d8,\n    1749px 1175px #ce93d8,\n    639px 1114px #ce93d8,\n    1591px 508px #ce93d8,\n    759px 1244px #ce93d8,\n    824px 380px #ce93d8,\n    942px 955px #ce93d8,\n    723px 732px #ce93d8,\n    113px 1369px #ce93d8,\n    203px 1739px #ce93d8,\n    868px 733px #ce93d8,\n    713px 971px #ce93d8,\n    341px 833px #ce93d8,\n    762px 824px #ce93d8,\n    1359px 310px #ce93d8,\n    1858px 1349px #ce93d8,\n    1531px 692px #ce93d8,\n    1075px 1512px #ce93d8,\n    1677px 142px #ce93d8,\n    1912px 1478px #ce93d8,\n    1810px 1078px #ce93d8,\n    426px 844px #ce93d8,\n    1426px 588px #ce93d8,\n    1909px 654px #ce93d8,\n    1107px 295px #ce93d8,\n    1351px 527px #ce93d8,\n    1393px 599px #ce93d8,\n    1379px 1068px #ce93d8,\n    228px 1846px #ce93d8,\n    1271px 374px #ce93d8,\n    1348px 612px #ce93d8,\n    7px 1301px #ce93d8,\n    1501px 1782px #ce93d8,\n    1795px 423px #ce93d8,\n    1475px 1918px #ce93d8,\n    1328px 1861px #ce93d8,\n    1624px 51px #ce93d8,\n    1791px 672px #ce93d8,\n    1594px 1467px #ce93d8,\n    1655px 1603px #ce93d8,\n    919px 850px #ce93d8,\n    523px 609px #ce93d8,\n    1196px 207px #ce93d8,\n    753px 410px #ce93d8,\n    686px 1097px #ce93d8,\n    1570px 133px #ce93d8,\n    1996px 1137px #ce93d8,\n    361px 116px #ce93d8,\n    1015px 462px #ce93d8,\n    76px 1143px #ce93d8,\n    491px 1818px #ce93d8,\n    1563px 795px #ce93d8,\n    982px 1721px #ce93d8,\n    831px 1204px #ce93d8,\n    1737px 589px #ce93d8,\n    861px 1579px #ce93d8,\n    1666px 130px #ce93d8,\n    698px 1799px #ce93d8,\n    726px 1519px #ce93d8,\n    109px 1208px #ce93d8,\n    1184px 1057px #ce93d8,\n    835px 451px #ce93d8,\n    896px 594px #ce93d8,\n    35px 893px #ce93d8,\n    895px 542px #ce93d8,\n    706px 225px #ce93d8,\n    56px 1040px #ce93d8,\n    1954px 108px #ce93d8,\n    1439px 1423px #ce93d8,\n    26px 1881px #ce93d8,\n    802px 1564px #ce93d8,\n    273px 708px #ce93d8,\n    40px 31px #ce93d8,\n    859px 108px #ce93d8;\n  animation: animStar 100s linear infinite;\n}\n\n#stars2:after {\n  content: \" \";\n  position: absolute;\n  top: 2000px;\n  width: 2px;\n  height: 2px;\n  background: transparent;\n  box-shadow:\n    1117px 1306px #ce93d8,\n    1078px 1783px #ce93d8,\n    1179px 1085px #ce93d8,\n    1145px 920px #ce93d8,\n    422px 1233px #ce93d8,\n    387px 98px #ce93d8,\n    1153px 637px #ce93d8,\n    1084px 782px #ce93d8,\n    476px 453px #ce93d8,\n    926px 1306px #ce93d8,\n    60px 1086px #ce93d8,\n    753px 1575px #ce93d8,\n    272px 1684px #ce93d8,\n    1285px 750px #ce93d8,\n    1416px 1327px #ce93d8,\n    1931px 473px #ce93d8,\n    736px 1395px #ce93d8,\n    1816px 763px #ce93d8,\n    438px 879px #ce93d8,\n    665px 1902px #ce93d8,\n    1341px 677px #ce93d8,\n    1404px 1073px #ce93d8,\n    100px 597px #ce93d8,\n    357px 1689px #ce93d8,\n    1044px 1342px #ce93d8,\n    1954px 502px #ce93d8,\n    1192px 1308px #ce93d8,\n    540px 1239px #ce93d8,\n    1360px 552px #ce93d8,\n    89px 752px #ce93d8,\n    659px 1253px #ce93d8,\n    62px 517px #ce93d8,\n    1375px 1705px #ce93d8,\n    1343px 1511px #ce93d8,\n    1659px 1922px #ce93d8,\n    1560px 289px #ce93d8,\n    1362px 1799px #ce93d8,\n    1886px 1480px #ce93d8,\n    1718px 1885px #ce93d8,\n    824px 738px #ce93d8,\n    1060px 1370px #ce93d8,\n    1781px 1171px #ce93d8,\n    255px 273px #ce93d8,\n    1197px 120px #ce93d8,\n    213px 7px #ce93d8,\n    1226px 1920px #ce93d8,\n    1844px 207px #ce93d8,\n    1675px 970px #ce93d8,\n    1435px 1283px #ce93d8,\n    37px 353px #ce93d8,\n    59px 417px #ce93d8,\n    921px 1602px #ce93d8,\n    1549px 1490px #ce93d8,\n    638px 1845px #ce93d8,\n    1328px 198px #ce93d8,\n    1050px 1149px #ce93d8,\n    1884px 711px #ce93d8,\n    333px 263px #ce93d8,\n    342px 1508px #ce93d8,\n    1388px 1810px #ce93d8,\n    1377px 1558px #ce93d8,\n    890px 487px #ce93d8,\n    1081px 759px #ce93d8,\n    890px 1515px #ce93d8,\n    911px 1284px #ce93d8,\n    335px 735px #ce93d8,\n    1140px 549px #ce93d8,\n    1239px 1064px #ce93d8,\n    226px 71px #ce93d8,\n    1100px 1278px #ce93d8,\n    1851px 1805px #ce93d8,\n    1370px 1999px #ce93d8,\n    1008px 1122px #ce93d8,\n    785px 813px #ce93d8,\n    1358px 601px #ce93d8,\n    1833px 1305px #ce93d8,\n    1768px 1304px #ce93d8,\n    1303px 532px #ce93d8,\n    860px 598px #ce93d8,\n    1329px 593px #ce93d8,\n    1038px 1088px #ce93d8,\n    408px 405px #ce93d8,\n    965px 82px #ce93d8,\n    1483px 1438px #ce93d8,\n    310px 1479px #ce93d8,\n    1786px 1500px #ce93d8,\n    1866px 852px #ce93d8,\n    18px 1757px #ce93d8,\n    1473px 1004px #ce93d8,\n    1542px 1933px #ce93d8,\n    633px 1970px #ce93d8,\n    1334px 1713px #ce93d8,\n    175px 28px #ce93d8,\n    592px 894px #ce93d8,\n    121px 1162px #ce93d8,\n    1601px 1567px #ce93d8,\n    1095px 657px #ce93d8,\n    640px 1233px #ce93d8,\n    1073px 1255px #ce93d8,\n    840px 1087px #ce93d8,\n    718px 250px #ce93d8,\n    967px 709px #ce93d8,\n    731px 239px #ce93d8,\n    1623px 593px #ce93d8,\n    1058px 1820px #ce93d8,\n    516px 1898px #ce93d8,\n    666px 12px #ce93d8,\n    1997px 1382px #ce93d8,\n    112px 1690px #ce93d8,\n    687px 1309px #ce93d8,\n    63px 539px #ce93d8,\n    185px 1897px #ce93d8,\n    1055px 1691px #ce93d8,\n    435px 1517px #ce93d8,\n    1175px 1119px #ce93d8,\n    1721px 133px #ce93d8,\n    1212px 47px #ce93d8,\n    166px 18px #ce93d8,\n    1416px 1652px #ce93d8,\n    1409px 1745px #ce93d8,\n    1357px 1232px #ce93d8,\n    1677px 1998px #ce93d8,\n    448px 1415px #ce93d8,\n    705px 1736px #ce93d8,\n    1031px 1466px #ce93d8,\n    543px 1651px #ce93d8,\n    1592px 1888px #ce93d8,\n    1749px 1175px #ce93d8,\n    639px 1114px #ce93d8,\n    1591px 508px #ce93d8,\n    759px 1244px #ce93d8,\n    824px 380px #ce93d8,\n    942px 955px #ce93d8,\n    723px 732px #ce93d8,\n    113px 1369px #ce93d8,\n    203px 1739px #ce93d8,\n    868px 733px #ce93d8,\n    713px 971px #ce93d8,\n    341px 833px #ce93d8,\n    762px 824px #ce93d8,\n    1359px 310px #ce93d8,\n    1858px 1349px #ce93d8,\n    1531px 692px #ce93d8,\n    1075px 1512px #ce93d8,\n    1677px 142px #ce93d8,\n    1912px 1478px #ce93d8,\n    1810px 1078px #ce93d8,\n    426px 844px #ce93d8,\n    1426px 588px #ce93d8,\n    1909px 654px #ce93d8,\n    1107px 295px #ce93d8,\n    1351px 527px #ce93d8,\n    1393px 599px #ce93d8,\n    1379px 1068px #ce93d8,\n    228px 1846px #ce93d8,\n    1271px 374px #ce93d8,\n    1348px 612px #ce93d8,\n    7px 1301px #ce93d8,\n    1501px 1782px #ce93d8,\n    1795px 423px #ce93d8,\n    1475px 1918px #ce93d8,\n    1328px 1861px #ce93d8,\n    1624px 51px #ce93d8,\n    1791px 672px #ce93d8,\n    1594px 1467px #ce93d8,\n    1655px 1603px #ce93d8,\n    919px 850px #ce93d8,\n    523px 609px #ce93d8,\n    1196px 207px #ce93d8,\n    753px 410px #ce93d8,\n    686px 1097px #ce93d8,\n    1570px 133px #ce93d8,\n    1996px 1137px #ce93d8,\n    361px 116px #ce93d8,\n    1015px 462px #ce93d8,\n    76px 1143px #ce93d8,\n    491px 1818px #ce93d8,\n    1563px 795px #ce93d8,\n    982px 1721px #ce93d8,\n    831px 1204px #ce93d8,\n    1737px 589px #ce93d8,\n    861px 1579px #ce93d8,\n    1666px 130px #ce93d8,\n    698px 1799px #ce93d8,\n    726px 1519px #ce93d8,\n    109px 1208px #ce93d8,\n    1184px 1057px #ce93d8,\n    835px 451px #ce93d8,\n    896px 594px #ce93d8,\n    35px 893px #ce93d8,\n    895px 542px #ce93d8,\n    706px 225px #ce93d8,\n    56px 1040px #ce93d8,\n    1954px 108px #ce93d8,\n    1439px 1423px #ce93d8,\n    26px 1881px #ce93d8,\n    802px 1564px #ce93d8,\n    273px 708px #ce93d8,\n    40px 31px #ce93d8,\n    859px 108px #ce93d8;\n}\n\n#stars3 {\n  width: 3px;\n  height: 3px;\n  background: transparent;\n  box-shadow:\n    940px 1360px #ce93d8,\n    1071px 539px #ce93d8,\n    1710px 1414px #ce93d8,\n    836px 299px #ce93d8,\n    1944px 1420px #ce93d8,\n    253px 1449px #ce93d8,\n    1257px 1250px #ce93d8,\n    1588px 1830px #ce93d8,\n    1077px 1204px #ce93d8,\n    273px 1081px #ce93d8,\n    1993px 766px #ce93d8,\n    1808px 479px #ce93d8,\n    917px 263px #ce93d8,\n    663px 1820px #ce93d8,\n    342px 1988px #ce93d8,\n    727px 1250px #ce93d8,\n    636px 1666px #ce93d8,\n    692px 1112px #ce93d8,\n    248px 1211px #ce93d8,\n    1422px 1121px #ce93d8,\n    881px 46px #ce93d8,\n    1531px 1977px #ce93d8,\n    1643px 1023px #ce93d8,\n    684px 1071px #ce93d8,\n    1142px 1873px #ce93d8,\n    292px 1313px #ce93d8,\n    256px 1237px #ce93d8,\n    89px 912px #ce93d8,\n    964px 1783px #ce93d8,\n    877px 760px #ce93d8,\n    1641px 1474px #ce93d8,\n    1492px 24px #ce93d8,\n    1776px 1642px #ce93d8,\n    183px 602px #ce93d8,\n    1998px 62px #ce93d8,\n    1560px 367px #ce93d8,\n    1333px 995px #ce93d8,\n    704px 1815px #ce93d8,\n    1809px 712px #ce93d8,\n    1503px 288px #ce93d8,\n    630px 556px #ce93d8,\n    1715px 125px #ce93d8,\n    353px 1878px #ce93d8,\n    975px 333px #ce93d8,\n    1740px 1409px #ce93d8,\n    1341px 1871px #ce93d8,\n    1279px 1064px #ce93d8,\n    169px 874px #ce93d8,\n    161px 528px #ce93d8,\n    1671px 1669px #ce93d8,\n    169px 632px #ce93d8,\n    547px 1724px #ce93d8,\n    1904px 110px #ce93d8,\n    679px 1670px #ce93d8,\n    196px 123px #ce93d8,\n    786px 871px #ce93d8,\n    1840px 324px #ce93d8,\n    356px 967px #ce93d8,\n    61px 549px #ce93d8,\n    99px 677px #ce93d8,\n    1719px 87px #ce93d8,\n    1713px 1990px #ce93d8,\n    1717px 1358px #ce93d8,\n    108px 1187px #ce93d8,\n    51px 869px #ce93d8,\n    1461px 902px #ce93d8,\n    1034px 891px #ce93d8,\n    962px 1881px #ce93d8,\n    1723px 595px #ce93d8,\n    479px 901px #ce93d8,\n    1546px 1823px #ce93d8,\n    285px 1208px #ce93d8,\n    1056px 347px #ce93d8,\n    261px 988px #ce93d8,\n    466px 990px #ce93d8,\n    1657px 648px #ce93d8,\n    1249px 933px #ce93d8,\n    1552px 1555px #ce93d8,\n    147px 62px #ce93d8,\n    292px 1157px #ce93d8,\n    1816px 423px #ce93d8,\n    1714px 757px #ce93d8,\n    1036px 961px #ce93d8,\n    1955px 710px #ce93d8,\n    1842px 516px #ce93d8,\n    479px 1870px #ce93d8,\n    1579px 1445px #ce93d8,\n    1225px 1309px #ce93d8,\n    1965px 566px #ce93d8,\n    1575px 1072px #ce93d8,\n    923px 329px #ce93d8,\n    651px 1514px #ce93d8,\n    865px 1100px #ce93d8,\n    782px 1873px #ce93d8,\n    115px 299px #ce93d8,\n    14px 1668px #ce93d8,\n    1666px 1817px #ce93d8,\n    1096px 1068px #ce93d8,\n    1462px 742px #ce93d8,\n    1384px 1750px #ce93d8;\n  animation: animStar 150s linear infinite;\n}\n\n#stars3:after {\n  content: \" \";\n  position: absolute;\n  top: 2000px;\n  width: 3px;\n  height: 3px;\n  background: transparent;\n  box-shadow:\n    940px 1360px #ce93d8,\n    1071px 539px #ce93d8,\n    1710px 1414px #ce93d8,\n    836px 299px #ce93d8,\n    1944px 1420px #ce93d8,\n    253px 1449px #ce93d8,\n    1257px 1250px #ce93d8,\n    1588px 1830px #ce93d8,\n    1077px 1204px #ce93d8,\n    273px 1081px #ce93d8,\n    1993px 766px #ce93d8,\n    1808px 479px #ce93d8,\n    917px 263px #ce93d8,\n    663px 1820px #ce93d8,\n    342px 1988px #ce93d8,\n    727px 1250px #ce93d8,\n    636px 1666px #ce93d8,\n    692px 1112px #ce93d8,\n    248px 1211px #ce93d8,\n    1422px 1121px #ce93d8,\n    881px 46px #ce93d8,\n    1531px 1977px #ce93d8,\n    1643px 1023px #ce93d8,\n    684px 1071px #ce93d8,\n    1142px 1873px #ce93d8,\n    292px 1313px #ce93d8,\n    256px 1237px #ce93d8,\n    89px 912px #ce93d8,\n    964px 1783px #ce93d8,\n    877px 760px #ce93d8,\n    1641px 1474px #ce93d8,\n    1492px 24px #ce93d8,\n    1776px 1642px #ce93d8,\n    183px 602px #ce93d8,\n    1998px 62px #ce93d8,\n    1560px 367px #ce93d8,\n    1333px 995px #ce93d8,\n    704px 1815px #ce93d8,\n    1809px 712px #ce93d8,\n    1503px 288px #ce93d8,\n    630px 556px #ce93d8,\n    1715px 125px #ce93d8,\n    353px 1878px #ce93d8,\n    975px 333px #ce93d8,\n    1740px 1409px #ce93d8,\n    1341px 1871px #ce93d8,\n    1279px 1064px #ce93d8,\n    169px 874px #ce93d8,\n    161px 528px #ce93d8,\n    1671px 1669px #ce93d8,\n    169px 632px #ce93d8,\n    547px 1724px #ce93d8,\n    1904px 110px #ce93d8,\n    679px 1670px #ce93d8,\n    196px 123px #ce93d8,\n    786px 871px #ce93d8,\n    1840px 324px #ce93d8,\n    356px 967px #ce93d8,\n    61px 549px #ce93d8,\n    99px 677px #ce93d8,\n    1719px 87px #ce93d8,\n    1713px 1990px #ce93d8,\n    1717px 1358px #ce93d8,\n    108px 1187px #ce93d8,\n    51px 869px #ce93d8,\n    1461px 902px #ce93d8,\n    1034px 891px #ce93d8,\n    962px 1881px #ce93d8,\n    1723px 595px #ce93d8,\n    479px 901px #ce93d8,\n    1546px 1823px #ce93d8,\n    285px 1208px #ce93d8,\n    1056px 347px #ce93d8,\n    261px 988px #ce93d8,\n    466px 990px #ce93d8,\n    1657px 648px #ce93d8,\n    1249px 933px #ce93d8,\n    1552px 1555px #ce93d8,\n    147px 62px #ce93d8,\n    292px 1157px #ce93d8,\n    1816px 423px #ce93d8,\n    1714px 757px #ce93d8,\n    1036px 961px #ce93d8,\n    1955px 710px #ce93d8,\n    1842px 516px #ce93d8,\n    479px 1870px #ce93d8,\n    1579px 1445px #ce93d8,\n    1225px 1309px #ce93d8,\n    1965px 566px #ce93d8,\n    1575px 1072px #ce93d8,\n    923px 329px #ce93d8,\n    651px 1514px #ce93d8,\n    865px 1100px #ce93d8,\n    782px 1873px #ce93d8,\n    115px 299px #ce93d8,\n    14px 1668px #ce93d8,\n    1666px 1817px #ce93d8,\n    1096px 1068px #ce93d8,\n    1462px 742px #ce93d8,\n    1384px 1750px #ce93d8;\n}\n\n#stars4 {\n  width: 1px;\n  height: 1px;\n  background: transparent;\n  box-shadow:\n    233px 1976px #ce93d8,\n    1196px 1119px #ce93d8,\n    646px 740px #ce93d8,\n    335px 645px #ce93d8,\n    1119px 1452px #ce93d8,\n    176px 1870px #ce93d8,\n    639px 1711px #ce93d8,\n    647px 1388px #ce93d8,\n    1516px 1108px #ce93d8,\n    464px 66px #ce93d8,\n    331px 344px #ce93d8,\n    772px 1189px #ce93d8,\n    1516px 1850px #ce93d8,\n    1500px 1463px #ce93d8,\n    1275px 876px #ce93d8,\n    1107px 645px #ce93d8,\n    977px 478px #ce93d8,\n    583px 1179px #ce93d8,\n    284px 395px #ce93d8,\n    1220px 461px #ce93d8,\n    1160px 249px #ce93d8,\n    196px 865px #ce93d8,\n    670px 1915px #ce93d8,\n    1449px 382px #ce93d8,\n    1191px 546px #ce93d8,\n    1329px 605px #ce93d8,\n    1945px 458px #ce93d8,\n    995px 749px #ce93d8,\n    1495px 861px #ce93d8,\n    708px 1731px #ce93d8,\n    348px 653px #ce93d8,\n    548px 1298px #ce93d8,\n    1606px 990px #ce93d8,\n    1049px 1204px #ce93d8,\n    253px 1501px #ce93d8,\n    1154px 166px #ce93d8,\n    1087px 104px #ce93d8,\n    1034px 1161px #ce93d8,\n    1681px 462px #ce93d8,\n    577px 1897px #ce93d8,\n    193px 1901px #ce93d8,\n    1701px 1755px #ce93d8,\n    864px 1297px #ce93d8,\n    800px 1289px #ce93d8,\n    676px 28px #ce93d8,\n    185px 1341px #ce93d8,\n    379px 1151px #ce93d8,\n    1224px 1725px #ce93d8,\n    280px 541px #ce93d8,\n    473px 1196px #ce93d8,\n    921px 1628px #ce93d8,\n    969px 432px #ce93d8,\n    1475px 758px #ce93d8,\n    1195px 993px #ce93d8,\n    876px 1840px #ce93d8,\n    1274px 1689px #ce93d8,\n    1977px 1101px #ce93d8,\n    837px 527px #ce93d8,\n    1785px 1610px #ce93d8,\n    1650px 1843px #ce93d8,\n    1127px 1508px #ce93d8,\n    401px 1050px #ce93d8,\n    51px 1105px #ce93d8,\n    545px 880px #ce93d8,\n    1786px 1672px #ce93d8,\n    318px 260px #ce93d8,\n    568px 254px #ce93d8,\n    1026px 1527px #ce93d8,\n    1242px 852px #ce93d8,\n    1785px 982px #ce93d8,\n    1318px 1071px #ce93d8,\n    398px 1061px #ce93d8,\n    1509px 257px #ce93d8,\n    599px 928px #ce93d8,\n    1195px 1800px #ce93d8,\n    1254px 906px #ce93d8,\n    141px 26px #ce93d8,\n    1384px 1502px #ce93d8,\n    476px 767px #ce93d8,\n    1973px 722px #ce93d8,\n    1339px 1031px #ce93d8,\n    778px 818px #ce93d8,\n    213px 1320px #ce93d8,\n    184px 221px #ce93d8,\n    983px 1911px #ce93d8,\n    923px 1439px #ce93d8,\n    1936px 581px #ce93d8,\n    1105px 625px #ce93d8,\n    325px 729px #ce93d8,\n    1475px 204px #ce93d8,\n    1483px 1564px #ce93d8,\n    1327px 1272px #ce93d8,\n    1187px 1944px #ce93d8,\n    1945px 1471px #ce93d8,\n    116px 960px #ce93d8,\n    1660px 1610px #ce93d8,\n    412px 1022px #ce93d8,\n    1552px 1516px #ce93d8,\n    1517px 1892px #ce93d8,\n    306px 829px #ce93d8,\n    1416px 462px #ce93d8,\n    1575px 1460px #ce93d8,\n    424px 1500px #ce93d8,\n    1530px 1169px #ce93d8,\n    1388px 1608px #ce93d8,\n    185px 416px #ce93d8,\n    634px 1446px #ce93d8,\n    767px 479px #ce93d8,\n    71px 426px #ce93d8,\n    1937px 145px #ce93d8,\n    1955px 1312px #ce93d8,\n    1811px 611px #ce93d8,\n    1145px 569px #ce93d8,\n    1460px 676px #ce93d8,\n    131px 1858px #ce93d8,\n    1557px 473px #ce93d8,\n    735px 130px #ce93d8,\n    112px 1531px #ce93d8,\n    1312px 305px #ce93d8,\n    409px 1032px #ce93d8,\n    149px 1964px #ce93d8,\n    535px 1215px #ce93d8,\n    1382px 630px #ce93d8,\n    1437px 1368px #ce93d8,\n    362px 1181px #ce93d8,\n    388px 181px #ce93d8,\n    274px 1287px #ce93d8,\n    1858px 1414px #ce93d8,\n    661px 1935px #ce93d8,\n    675px 1205px #ce93d8,\n    1829px 1725px #ce93d8,\n    1937px 1145px #ce93d8,\n    237px 908px #ce93d8,\n    1059px 1185px #ce93d8,\n    824px 1248px #ce93d8,\n    1167px 1730px #ce93d8,\n    180px 1961px #ce93d8,\n    1663px 203px #ce93d8,\n    374px 221px #ce93d8,\n    724px 1883px #ce93d8,\n    970px 1362px #ce93d8,\n    832px 505px #ce93d8,\n    313px 233px #ce93d8,\n    1909px 597px #ce93d8,\n    434px 201px #ce93d8,\n    587px 995px #ce93d8,\n    1833px 623px #ce93d8,\n    1464px 561px #ce93d8,\n    231px 593px #ce93d8,\n    1558px 1433px #ce93d8,\n    1986px 1767px #ce93d8,\n    1753px 1728px #ce93d8,\n    1153px 1623px #ce93d8,\n    249px 229px #ce93d8,\n    1503px 1186px #ce93d8,\n    1784px 137px #ce93d8,\n    841px 403px #ce93d8,\n    1400px 354px #ce93d8,\n    197px 499px #ce93d8,\n    1188px 681px #ce93d8,\n    158px 391px #ce93d8,\n    443px 1099px #ce93d8,\n    723px 1445px #ce93d8,\n    1408px 1235px #ce93d8,\n    1908px 195px #ce93d8,\n    271px 891px #ce93d8,\n    469px 1693px #ce93d8,\n    580px 11px #ce93d8,\n    1533px 70px #ce93d8,\n    859px 761px #ce93d8,\n    1510px 1844px #ce93d8,\n    421px 558px #ce93d8,\n    1132px 1453px #ce93d8,\n    757px 1987px #ce93d8,\n    212px 293px #ce93d8,\n    569px 323px #ce93d8,\n    1404px 1394px #ce93d8,\n    252px 1386px #ce93d8,\n    1668px 1857px #ce93d8,\n    123px 1684px #ce93d8,\n    105px 490px #ce93d8,\n    1083px 1769px #ce93d8,\n    1071px 1953px #ce93d8,\n    1271px 1159px #ce93d8,\n    699px 1491px #ce93d8,\n    1744px 1997px #ce93d8,\n    1868px 1973px #ce93d8,\n    1438px 1449px #ce93d8,\n    1222px 1921px #ce93d8,\n    1328px 1210px #ce93d8,\n    438px 873px #ce93d8,\n    809px 780px #ce93d8,\n    491px 1524px #ce93d8,\n    447px 1830px #ce93d8,\n    927px 1936px #ce93d8,\n    564px 691px #ce93d8,\n    1784px 1747px #ce93d8,\n    1978px 1722px #ce93d8,\n    1599px 1480px #ce93d8,\n    1276px 729px #ce93d8,\n    731px 1174px #ce93d8,\n    1586px 1711px #ce93d8,\n    451px 1340px #ce93d8,\n    1075px 1899px #ce93d8,\n    13px 575px #ce93d8,\n    309px 1340px #ce93d8,\n    981px 183px #ce93d8,\n    248px 1315px #ce93d8,\n    849px 80px #ce93d8,\n    1754px 1540px #ce93d8,\n    73px 1432px #ce93d8,\n    1208px 1828px #ce93d8,\n    65px 575px #ce93d8,\n    1098px 730px #ce93d8,\n    127px 1358px #ce93d8,\n    185px 19px #ce93d8,\n    1222px 1679px #ce93d8,\n    1122px 315px #ce93d8,\n    1906px 452px #ce93d8,\n    761px 284px #ce93d8,\n    813px 492px #ce93d8,\n    1344px 843px #ce93d8,\n    118px 1834px #ce93d8,\n    1620px 359px #ce93d8,\n    1755px 1246px #ce93d8,\n    299px 1076px #ce93d8,\n    1746px 158px #ce93d8,\n    6px 1635px #ce93d8,\n    143px 190px #ce93d8,\n    101px 468px #ce93d8,\n    137px 971px #ce93d8,\n    1221px 1929px #ce93d8,\n    1752px 650px #ce93d8,\n    1635px 1761px #ce93d8,\n    1522px 833px #ce93d8,\n    908px 153px #ce93d8,\n    1044px 350px #ce93d8,\n    1151px 1940px #ce93d8,\n    822px 210px #ce93d8,\n    1774px 310px #ce93d8,\n    796px 1447px #ce93d8,\n    1069px 1903px #ce93d8,\n    217px 565px #ce93d8,\n    662px 1370px #ce93d8,\n    1876px 1570px #ce93d8,\n    847px 46px #ce93d8,\n    1042px 1689px #ce93d8,\n    1584px 1434px #ce93d8,\n    1791px 908px #ce93d8,\n    973px 908px #ce93d8,\n    793px 747px #ce93d8,\n    122px 483px #ce93d8,\n    1137px 1374px #ce93d8,\n    1757px 1791px #ce93d8,\n    513px 225px #ce93d8,\n    63px 731px #ce93d8,\n    1179px 1926px #ce93d8,\n    346px 18px #ce93d8,\n    589px 175px #ce93d8,\n    87px 302px #ce93d8,\n    380px 1295px #ce93d8,\n    450px 921px #ce93d8,\n    1667px 1973px #ce93d8,\n    1495px 1373px #ce93d8,\n    1462px 1850px #ce93d8,\n    540px 288px #ce93d8,\n    1208px 1051px #ce93d8,\n    1554px 1095px #ce93d8,\n    1009px 1516px #ce93d8,\n    181px 572px #ce93d8,\n    165px 387px #ce93d8,\n    549px 1835px #ce93d8,\n    960px 16px #ce93d8,\n    1360px 403px #ce93d8,\n    1251px 43px #ce93d8,\n    1905px 1813px #ce93d8,\n    1106px 866px #ce93d8,\n    1809px 277px #ce93d8,\n    1828px 1720px #ce93d8,\n    295px 1610px #ce93d8,\n    523px 166px #ce93d8,\n    1069px 692px #ce93d8,\n    1292px 217px #ce93d8,\n    11px 1721px #ce93d8,\n    99px 1045px #ce93d8,\n    51px 1584px #ce93d8,\n    1053px 266px #ce93d8,\n    1287px 1235px #ce93d8,\n    747px 1722px #ce93d8,\n    1542px 736px #ce93d8,\n    1256px 18px #ce93d8,\n    102px 609px #ce93d8,\n    586px 1339px #ce93d8,\n    1843px 1697px #ce93d8,\n    824px 1687px #ce93d8,\n    1124px 882px #ce93d8,\n    395px 501px #ce93d8,\n    1456px 672px #ce93d8,\n    1472px 1648px #ce93d8,\n    1326px 1164px #ce93d8,\n    777px 1672px #ce93d8,\n    81px 345px #ce93d8,\n    91px 386px #ce93d8,\n    243px 411px #ce93d8,\n    1560px 90px #ce93d8,\n    6px 1771px #ce93d8,\n    1601px 616px #ce93d8,\n    1220px 1808px #ce93d8,\n    1160px 836px #ce93d8,\n    246px 1777px #ce93d8,\n    456px 863px #ce93d8,\n    97px 1138px #ce93d8,\n    1811px 942px #ce93d8,\n    213px 414px #ce93d8,\n    891px 392px #ce93d8,\n    1044px 927px #ce93d8,\n    1856px 216px #ce93d8,\n    957px 347px #ce93d8,\n    1486px 406px #ce93d8,\n    838px 912px #ce93d8,\n    803px 361px #ce93d8,\n    564px 826px #ce93d8,\n    1597px 949px #ce93d8,\n    1206px 289px #ce93d8,\n    33px 1035px #ce93d8,\n    1762px 1377px #ce93d8,\n    789px 1815px #ce93d8,\n    1594px 1342px #ce93d8,\n    1668px 880px #ce93d8,\n    1539px 1581px #ce93d8,\n    1547px 53px #ce93d8,\n    861px 1433px #ce93d8,\n    693px 1618px #ce93d8,\n    1762px 782px #ce93d8,\n    1568px 682px #ce93d8,\n    1126px 1762px #ce93d8,\n    1242px 134px #ce93d8,\n    495px 959px #ce93d8,\n    1606px 219px #ce93d8,\n    1878px 1415px #ce93d8,\n    1652px 797px #ce93d8,\n    782px 1903px #ce93d8,\n    1774px 1133px #ce93d8,\n    1430px 408px #ce93d8,\n    265px 394px #ce93d8,\n    890px 336px #ce93d8,\n    1051px 311px #ce93d8,\n    461px 1559px #ce93d8,\n    1931px 91px #ce93d8,\n    1160px 380px #ce93d8,\n    1442px 1058px #ce93d8,\n    1157px 364px #ce93d8,\n    586px 227px #ce93d8,\n    1365px 715px #ce93d8,\n    1658px 1655px #ce93d8,\n    1923px 1664px #ce93d8,\n    1023px 1844px #ce93d8,\n    1939px 1367px #ce93d8,\n    1203px 1305px #ce93d8,\n    359px 642px #ce93d8,\n    1056px 425px #ce93d8,\n    787px 202px #ce93d8,\n    1609px 1850px #ce93d8,\n    1964px 200px #ce93d8,\n    1537px 586px #ce93d8,\n    1589px 903px #ce93d8,\n    1063px 1694px #ce93d8,\n    760px 1185px #ce93d8,\n    597px 1396px #ce93d8,\n    294px 452px #ce93d8,\n    433px 818px #ce93d8,\n    199px 840px #ce93d8,\n    1332px 1937px #ce93d8,\n    169px 1907px #ce93d8,\n    591px 834px #ce93d8,\n    1716px 1032px #ce93d8,\n    45px 1879px #ce93d8,\n    686px 1469px #ce93d8,\n    1520px 475px #ce93d8,\n    1122px 859px #ce93d8,\n    973px 1541px #ce93d8,\n    269px 477px #ce93d8,\n    1390px 716px #ce93d8,\n    1791px 783px #ce93d8,\n    824px 2000px #ce93d8,\n    1211px 1717px #ce93d8,\n    1008px 1587px #ce93d8,\n    1422px 204px #ce93d8,\n    234px 556px #ce93d8,\n    506px 550px #ce93d8,\n    942px 1670px #ce93d8,\n    397px 853px #ce93d8,\n    599px 795px #ce93d8,\n    762px 1926px #ce93d8,\n    1202px 1424px #ce93d8,\n    135px 1316px #ce93d8,\n    1442px 1692px #ce93d8,\n    977px 652px #ce93d8,\n    564px 1648px #ce93d8,\n    997px 1474px #ce93d8,\n    67px 1366px #ce93d8,\n    1860px 1451px #ce93d8,\n    1105px 772px #ce93d8,\n    1886px 1396px #ce93d8,\n    1510px 658px #ce93d8,\n    976px 1544px #ce93d8,\n    894px 543px #ce93d8,\n    1098px 1189px #ce93d8,\n    690px 77px #ce93d8,\n    770px 733px #ce93d8,\n    557px 1403px #ce93d8,\n    1758px 1623px #ce93d8,\n    1341px 812px #ce93d8,\n    699px 967px #ce93d8,\n    277px 866px #ce93d8,\n    1526px 1828px #ce93d8,\n    8px 977px #ce93d8,\n    1707px 952px #ce93d8,\n    12px 1900px #ce93d8,\n    72px 921px #ce93d8,\n    496px 1067px #ce93d8,\n    1288px 1749px #ce93d8,\n    273px 984px #ce93d8,\n    1197px 1991px #ce93d8,\n    242px 789px #ce93d8,\n    903px 1035px #ce93d8,\n    480px 1492px #ce93d8,\n    102px 1331px #ce93d8,\n    738px 1343px #ce93d8,\n    560px 1475px #ce93d8,\n    367px 846px #ce93d8,\n    1420px 962px #ce93d8,\n    1976px 892px #ce93d8,\n    1911px 1763px #ce93d8,\n    1639px 1002px #ce93d8,\n    437px 1522px #ce93d8,\n    1906px 1025px #ce93d8,\n    730px 1364px #ce93d8,\n    1127px 521px #ce93d8,\n    1401px 1792px #ce93d8,\n    1954px 1066px #ce93d8,\n    232px 250px #ce93d8,\n    1685px 660px #ce93d8,\n    1011px 999px #ce93d8,\n    1970px 790px #ce93d8,\n    750px 499px #ce93d8,\n    1738px 660px #ce93d8,\n    1621px 1849px #ce93d8,\n    446px 52px #ce93d8,\n    1055px 1396px #ce93d8,\n    1165px 1497px #ce93d8,\n    1740px 1425px #ce93d8,\n    1012px 1920px #ce93d8,\n    1258px 1560px #ce93d8,\n    1020px 1152px #ce93d8,\n    362px 673px #ce93d8,\n    1065px 975px #ce93d8,\n    582px 755px #ce93d8,\n    1271px 1479px #ce93d8,\n    719px 548px #ce93d8,\n    1602px 879px #ce93d8,\n    590px 499px #ce93d8,\n    721px 1412px #ce93d8,\n    1180px 113px #ce93d8,\n    1801px 1961px #ce93d8,\n    589px 941px #ce93d8,\n    883px 476px #ce93d8,\n    214px 890px #ce93d8,\n    1028px 892px #ce93d8,\n    1107px 1832px #ce93d8,\n    944px 361px #ce93d8,\n    480px 1453px #ce93d8,\n    1466px 683px #ce93d8,\n    981px 745px #ce93d8,\n    1968px 828px #ce93d8,\n    657px 1830px #ce93d8,\n    11px 1338px #ce93d8,\n    179px 730px #ce93d8,\n    1713px 197px #ce93d8,\n    51px 955px #ce93d8,\n    1243px 319px #ce93d8,\n    1175px 624px #ce93d8,\n    446px 46px #ce93d8,\n    5px 1158px #ce93d8,\n    82px 1352px #ce93d8,\n    1877px 402px #ce93d8,\n    708px 1778px #ce93d8,\n    903px 1625px #ce93d8,\n    1824px 352px #ce93d8,\n    1229px 140px #ce93d8,\n    1518px 24px #ce93d8,\n    1017px 512px #ce93d8,\n    515px 699px #ce93d8,\n    295px 265px #ce93d8,\n    69px 1773px #ce93d8,\n    1640px 1163px #ce93d8,\n    536px 342px #ce93d8,\n    970px 1766px #ce93d8,\n    560px 1416px #ce93d8,\n    577px 193px #ce93d8,\n    469px 9px #ce93d8,\n    466px 276px #ce93d8,\n    711px 853px #ce93d8,\n    401px 685px #ce93d8,\n    85px 506px #ce93d8,\n    865px 558px #ce93d8,\n    631px 105px #ce93d8,\n    887px 866px #ce93d8,\n    1704px 1001px #ce93d8,\n    1051px 1199px #ce93d8,\n    275px 1909px #ce93d8,\n    1462px 829px #ce93d8,\n    375px 1057px #ce93d8,\n    1531px 1501px #ce93d8,\n    205px 403px #ce93d8,\n    33px 1869px #ce93d8,\n    967px 1176px #ce93d8,\n    376px 863px #ce93d8,\n    1769px 1545px #ce93d8,\n    535px 51px #ce93d8,\n    1972px 1569px #ce93d8,\n    1773px 960px #ce93d8,\n    487px 620px #ce93d8,\n    1660px 687px #ce93d8,\n    1632px 972px #ce93d8,\n    1362px 42px #ce93d8,\n    479px 1655px #ce93d8,\n    1531px 1808px #ce93d8,\n    1450px 1412px #ce93d8,\n    1549px 170px #ce93d8,\n    1904px 1305px #ce93d8,\n    1209px 48px #ce93d8,\n    1933px 820px #ce93d8,\n    1623px 595px #ce93d8,\n    48px 643px #ce93d8,\n    179px 1754px #ce93d8,\n    589px 1032px #ce93d8,\n    1199px 356px #ce93d8,\n    1755px 1418px #ce93d8,\n    780px 1174px #ce93d8,\n    1905px 758px #ce93d8,\n    1567px 713px #ce93d8,\n    1372px 705px #ce93d8,\n    456px 654px #ce93d8,\n    759px 690px #ce93d8,\n    452px 673px #ce93d8,\n    993px 1610px #ce93d8,\n    1271px 188px #ce93d8,\n    343px 1750px #ce93d8,\n    1943px 1735px #ce93d8,\n    1717px 853px #ce93d8,\n    1247px 303px #ce93d8,\n    1314px 1895px #ce93d8,\n    1203px 489px #ce93d8,\n    741px 469px #ce93d8,\n    4px 246px #ce93d8,\n    1515px 115px #ce93d8,\n    606px 218px #ce93d8,\n    1966px 1471px #ce93d8,\n    177px 87px #ce93d8,\n    1575px 588px #ce93d8,\n    1136px 1386px #ce93d8,\n    70px 1868px #ce93d8,\n    1053px 18px #ce93d8,\n    1124px 721px #ce93d8,\n    1748px 1181px #ce93d8,\n    191px 1387px #ce93d8,\n    1931px 840px #ce93d8,\n    1088px 1603px #ce93d8,\n    634px 1255px #ce93d8,\n    814px 1434px #ce93d8,\n    585px 64px #ce93d8,\n    1074px 1618px #ce93d8,\n    1692px 761px #ce93d8,\n    651px 643px #ce93d8,\n    193px 335px #ce93d8,\n    1103px 1447px #ce93d8,\n    491px 1142px #ce93d8,\n    521px 408px #ce93d8,\n    536px 340px #ce93d8,\n    411px 1091px #ce93d8,\n    1646px 193px #ce93d8,\n    1595px 1285px #ce93d8,\n    870px 1349px #ce93d8,\n    1085px 1013px #ce93d8,\n    204px 1864px #ce93d8,\n    1359px 299px #ce93d8,\n    807px 964px #ce93d8,\n    219px 509px #ce93d8,\n    36px 1227px #ce93d8,\n    702px 1873px #ce93d8,\n    1471px 934px #ce93d8,\n    1763px 792px #ce93d8,\n    973px 1957px #ce93d8,\n    987px 68px #ce93d8,\n    593px 1282px #ce93d8,\n    1900px 607px #ce93d8,\n    407px 1659px #ce93d8,\n    587px 17px #ce93d8,\n    632px 158px #ce93d8;\n  animation: animStar 600s linear infinite;\n}\n\n#stars4:after {\n  content: \" \";\n  position: absolute;\n  top: 2000px;\n  width: 1px;\n  height: 1px;\n  background: transparent;\n  box-shadow:\n    233px 1976px #ce93d8,\n    1196px 1119px #ce93d8,\n    646px 740px #ce93d8,\n    335px 645px #ce93d8,\n    1119px 1452px #ce93d8,\n    176px 1870px #ce93d8,\n    639px 1711px #ce93d8,\n    647px 1388px #ce93d8,\n    1516px 1108px #ce93d8,\n    464px 66px #ce93d8,\n    331px 344px #ce93d8,\n    772px 1189px #ce93d8,\n    1516px 1850px #ce93d8,\n    1500px 1463px #ce93d8,\n    1275px 876px #ce93d8,\n    1107px 645px #ce93d8,\n    977px 478px #ce93d8,\n    583px 1179px #ce93d8,\n    284px 395px #ce93d8,\n    1220px 461px #ce93d8,\n    1160px 249px #ce93d8,\n    196px 865px #ce93d8,\n    670px 1915px #ce93d8,\n    1449px 382px #ce93d8,\n    1191px 546px #ce93d8,\n    1329px 605px #ce93d8,\n    1945px 458px #ce93d8,\n    995px 749px #ce93d8,\n    1495px 861px #ce93d8,\n    708px 1731px #ce93d8,\n    348px 653px #ce93d8,\n    548px 1298px #ce93d8,\n    1606px 990px #ce93d8,\n    1049px 1204px #ce93d8,\n    253px 1501px #ce93d8,\n    1154px 166px #ce93d8,\n    1087px 104px #ce93d8,\n    1034px 1161px #ce93d8,\n    1681px 462px #ce93d8,\n    577px 1897px #ce93d8,\n    193px 1901px #ce93d8,\n    1701px 1755px #ce93d8,\n    864px 1297px #ce93d8,\n    800px 1289px #ce93d8,\n    676px 28px #ce93d8,\n    185px 1341px #ce93d8,\n    379px 1151px #ce93d8,\n    1224px 1725px #ce93d8,\n    280px 541px #ce93d8,\n    473px 1196px #ce93d8,\n    921px 1628px #ce93d8,\n    969px 432px #ce93d8,\n    1475px 758px #ce93d8,\n    1195px 993px #ce93d8,\n    876px 1840px #ce93d8,\n    1274px 1689px #ce93d8,\n    1977px 1101px #ce93d8,\n    837px 527px #ce93d8,\n    1785px 1610px #ce93d8,\n    1650px 1843px #ce93d8,\n    1127px 1508px #ce93d8,\n    401px 1050px #ce93d8,\n    51px 1105px #ce93d8,\n    545px 880px #ce93d8,\n    1786px 1672px #ce93d8,\n    318px 260px #ce93d8,\n    568px 254px #ce93d8,\n    1026px 1527px #ce93d8,\n    1242px 852px #ce93d8,\n    1785px 982px #ce93d8,\n    1318px 1071px #ce93d8,\n    398px 1061px #ce93d8,\n    1509px 257px #ce93d8,\n    599px 928px #ce93d8,\n    1195px 1800px #ce93d8,\n    1254px 906px #ce93d8,\n    141px 26px #ce93d8,\n    1384px 1502px #ce93d8,\n    476px 767px #ce93d8,\n    1973px 722px #ce93d8,\n    1339px 1031px #ce93d8,\n    778px 818px #ce93d8,\n    213px 1320px #ce93d8,\n    184px 221px #ce93d8,\n    983px 1911px #ce93d8,\n    923px 1439px #ce93d8,\n    1936px 581px #ce93d8,\n    1105px 625px #ce93d8,\n    325px 729px #ce93d8,\n    1475px 204px #ce93d8,\n    1483px 1564px #ce93d8,\n    1327px 1272px #ce93d8,\n    1187px 1944px #ce93d8,\n    1945px 1471px #ce93d8,\n    116px 960px #ce93d8,\n    1660px 1610px #ce93d8,\n    412px 1022px #ce93d8,\n    1552px 1516px #ce93d8,\n    1517px 1892px #ce93d8,\n    306px 829px #ce93d8,\n    1416px 462px #ce93d8,\n    1575px 1460px #ce93d8,\n    424px 1500px #ce93d8,\n    1530px 1169px #ce93d8,\n    1388px 1608px #ce93d8,\n    185px 416px #ce93d8,\n    634px 1446px #ce93d8,\n    767px 479px #ce93d8,\n    71px 426px #ce93d8,\n    1937px 145px #ce93d8,\n    1955px 1312px #ce93d8,\n    1811px 611px #ce93d8,\n    1145px 569px #ce93d8,\n    1460px 676px #ce93d8,\n    131px 1858px #ce93d8,\n    1557px 473px #ce93d8,\n    735px 130px #ce93d8,\n    112px 1531px #ce93d8,\n    1312px 305px #ce93d8,\n    409px 1032px #ce93d8,\n    149px 1964px #ce93d8,\n    535px 1215px #ce93d8,\n    1382px 630px #ce93d8,\n    1437px 1368px #ce93d8,\n    362px 1181px #ce93d8,\n    388px 181px #ce93d8,\n    274px 1287px #ce93d8,\n    1858px 1414px #ce93d8,\n    661px 1935px #ce93d8,\n    675px 1205px #ce93d8,\n    1829px 1725px #ce93d8,\n    1937px 1145px #ce93d8,\n    237px 908px #ce93d8,\n    1059px 1185px #ce93d8,\n    824px 1248px #ce93d8,\n    1167px 1730px #ce93d8,\n    180px 1961px #ce93d8,\n    1663px 203px #ce93d8,\n    374px 221px #ce93d8,\n    724px 1883px #ce93d8,\n    970px 1362px #ce93d8,\n    832px 505px #ce93d8,\n    313px 233px #ce93d8,\n    1909px 597px #ce93d8,\n    434px 201px #ce93d8,\n    587px 995px #ce93d8,\n    1833px 623px #ce93d8,\n    1464px 561px #ce93d8,\n    231px 593px #ce93d8,\n    1558px 1433px #ce93d8,\n    1986px 1767px #ce93d8,\n    1753px 1728px #ce93d8,\n    1153px 1623px #ce93d8,\n    249px 229px #ce93d8,\n    1503px 1186px #ce93d8,\n    1784px 137px #ce93d8,\n    841px 403px #ce93d8,\n    1400px 354px #ce93d8,\n    197px 499px #ce93d8,\n    1188px 681px #ce93d8,\n    158px 391px #ce93d8,\n    443px 1099px #ce93d8,\n    723px 1445px #ce93d8,\n    1408px 1235px #ce93d8,\n    1908px 195px #ce93d8,\n    271px 891px #ce93d8,\n    469px 1693px #ce93d8,\n    580px 11px #ce93d8,\n    1533px 70px #ce93d8,\n    859px 761px #ce93d8,\n    1510px 1844px #ce93d8,\n    421px 558px #ce93d8,\n    1132px 1453px #ce93d8,\n    757px 1987px #ce93d8,\n    212px 293px #ce93d8,\n    569px 323px #ce93d8,\n    1404px 1394px #ce93d8,\n    252px 1386px #ce93d8,\n    1668px 1857px #ce93d8,\n    123px 1684px #ce93d8,\n    105px 490px #ce93d8,\n    1083px 1769px #ce93d8,\n    1071px 1953px #ce93d8,\n    1271px 1159px #ce93d8,\n    699px 1491px #ce93d8,\n    1744px 1997px #ce93d8,\n    1868px 1973px #ce93d8,\n    1438px 1449px #ce93d8,\n    1222px 1921px #ce93d8,\n    1328px 1210px #ce93d8,\n    438px 873px #ce93d8,\n    809px 780px #ce93d8,\n    491px 1524px #ce93d8,\n    447px 1830px #ce93d8,\n    927px 1936px #ce93d8,\n    564px 691px #ce93d8,\n    1784px 1747px #ce93d8,\n    1978px 1722px #ce93d8,\n    1599px 1480px #ce93d8,\n    1276px 729px #ce93d8,\n    731px 1174px #ce93d8,\n    1586px 1711px #ce93d8,\n    451px 1340px #ce93d8,\n    1075px 1899px #ce93d8,\n    13px 575px #ce93d8,\n    309px 1340px #ce93d8,\n    981px 183px #ce93d8,\n    248px 1315px #ce93d8,\n    849px 80px #ce93d8,\n    1754px 1540px #ce93d8,\n    73px 1432px #ce93d8,\n    1208px 1828px #ce93d8,\n    65px 575px #ce93d8,\n    1098px 730px #ce93d8,\n    127px 1358px #ce93d8,\n    185px 19px #ce93d8,\n    1222px 1679px #ce93d8,\n    1122px 315px #ce93d8,\n    1906px 452px #ce93d8,\n    761px 284px #ce93d8,\n    813px 492px #ce93d8,\n    1344px 843px #ce93d8,\n    118px 1834px #ce93d8,\n    1620px 359px #ce93d8,\n    1755px 1246px #ce93d8,\n    299px 1076px #ce93d8,\n    1746px 158px #ce93d8,\n    6px 1635px #ce93d8,\n    143px 190px #ce93d8,\n    101px 468px #ce93d8,\n    137px 971px #ce93d8,\n    1221px 1929px #ce93d8,\n    1752px 650px #ce93d8,\n    1635px 1761px #ce93d8,\n    1522px 833px #ce93d8,\n    908px 153px #ce93d8,\n    1044px 350px #ce93d8,\n    1151px 1940px #ce93d8,\n    822px 210px #ce93d8,\n    1774px 310px #ce93d8,\n    796px 1447px #ce93d8,\n    1069px 1903px #ce93d8,\n    217px 565px #ce93d8,\n    662px 1370px #ce93d8,\n    1876px 1570px #ce93d8,\n    847px 46px #ce93d8,\n    1042px 1689px #ce93d8,\n    1584px 1434px #ce93d8,\n    1791px 908px #ce93d8,\n    973px 908px #ce93d8,\n    793px 747px #ce93d8,\n    122px 483px #ce93d8,\n    1137px 1374px #ce93d8,\n    1757px 1791px #ce93d8,\n    513px 225px #ce93d8,\n    63px 731px #ce93d8,\n    1179px 1926px #ce93d8,\n    346px 18px #ce93d8,\n    589px 175px #ce93d8,\n    87px 302px #ce93d8,\n    380px 1295px #ce93d8,\n    450px 921px #ce93d8,\n    1667px 1973px #ce93d8,\n    1495px 1373px #ce93d8,\n    1462px 1850px #ce93d8,\n    540px 288px #ce93d8,\n    1208px 1051px #ce93d8,\n    1554px 1095px #ce93d8,\n    1009px 1516px #ce93d8,\n    181px 572px #ce93d8,\n    165px 387px #ce93d8,\n    549px 1835px #ce93d8,\n    960px 16px #ce93d8,\n    1360px 403px #ce93d8,\n    1251px 43px #ce93d8,\n    1905px 1813px #ce93d8,\n    1106px 866px #ce93d8,\n    1809px 277px #ce93d8,\n    1828px 1720px #ce93d8,\n    295px 1610px #ce93d8,\n    523px 166px #ce93d8,\n    1069px 692px #ce93d8,\n    1292px 217px #ce93d8,\n    11px 1721px #ce93d8,\n    99px 1045px #ce93d8,\n    51px 1584px #ce93d8,\n    1053px 266px #ce93d8,\n    1287px 1235px #ce93d8,\n    747px 1722px #ce93d8,\n    1542px 736px #ce93d8,\n    1256px 18px #ce93d8,\n    102px 609px #ce93d8,\n    586px 1339px #ce93d8,\n    1843px 1697px #ce93d8,\n    824px 1687px #ce93d8,\n    1124px 882px #ce93d8,\n    395px 501px #ce93d8,\n    1456px 672px #ce93d8,\n    1472px 1648px #ce93d8,\n    1326px 1164px #ce93d8,\n    777px 1672px #ce93d8,\n    81px 345px #ce93d8,\n    91px 386px #ce93d8,\n    243px 411px #ce93d8,\n    1560px 90px #ce93d8,\n    6px 1771px #ce93d8,\n    1601px 616px #ce93d8,\n    1220px 1808px #ce93d8,\n    1160px 836px #ce93d8,\n    246px 1777px #ce93d8,\n    456px 863px #ce93d8,\n    97px 1138px #ce93d8,\n    1811px 942px #ce93d8,\n    213px 414px #ce93d8,\n    891px 392px #ce93d8,\n    1044px 927px #ce93d8,\n    1856px 216px #ce93d8,\n    957px 347px #ce93d8,\n    1486px 406px #ce93d8,\n    838px 912px #ce93d8,\n    803px 361px #ce93d8,\n    564px 826px #ce93d8,\n    1597px 949px #ce93d8,\n    1206px 289px #ce93d8,\n    33px 1035px #ce93d8,\n    1762px 1377px #ce93d8,\n    789px 1815px #ce93d8,\n    1594px 1342px #ce93d8,\n    1668px 880px #ce93d8,\n    1539px 1581px #ce93d8,\n    1547px 53px #ce93d8,\n    861px 1433px #ce93d8,\n    693px 1618px #ce93d8,\n    1762px 782px #ce93d8,\n    1568px 682px #ce93d8,\n    1126px 1762px #ce93d8,\n    1242px 134px #ce93d8,\n    495px 959px #ce93d8,\n    1606px 219px #ce93d8,\n    1878px 1415px #ce93d8,\n    1652px 797px #ce93d8,\n    782px 1903px #ce93d8,\n    1774px 1133px #ce93d8,\n    1430px 408px #ce93d8,\n    265px 394px #ce93d8,\n    890px 336px #ce93d8,\n    1051px 311px #ce93d8,\n    461px 1559px #ce93d8,\n    1931px 91px #ce93d8,\n    1160px 380px #ce93d8,\n    1442px 1058px #ce93d8,\n    1157px 364px #ce93d8,\n    586px 227px #ce93d8,\n    1365px 715px #ce93d8,\n    1658px 1655px #ce93d8,\n    1923px 1664px #ce93d8,\n    1023px 1844px #ce93d8,\n    1939px 1367px #ce93d8,\n    1203px 1305px #ce93d8,\n    359px 642px #ce93d8,\n    1056px 425px #ce93d8,\n    787px 202px #ce93d8,\n    1609px 1850px #ce93d8,\n    1964px 200px #ce93d8,\n    1537px 586px #ce93d8,\n    1589px 903px #ce93d8,\n    1063px 1694px #ce93d8,\n    760px 1185px #ce93d8,\n    597px 1396px #ce93d8,\n    294px 452px #ce93d8,\n    433px 818px #ce93d8,\n    199px 840px #ce93d8,\n    1332px 1937px #ce93d8,\n    169px 1907px #ce93d8,\n    591px 834px #ce93d8,\n    1716px 1032px #ce93d8,\n    45px 1879px #ce93d8,\n    686px 1469px #ce93d8,\n    1520px 475px #ce93d8,\n    1122px 859px #ce93d8,\n    973px 1541px #ce93d8,\n    269px 477px #ce93d8,\n    1390px 716px #ce93d8,\n    1791px 783px #ce93d8,\n    824px 2000px #ce93d8,\n    1211px 1717px #ce93d8,\n    1008px 1587px #ce93d8,\n    1422px 204px #ce93d8,\n    234px 556px #ce93d8,\n    506px 550px #ce93d8,\n    942px 1670px #ce93d8,\n    397px 853px #ce93d8,\n    599px 795px #ce93d8,\n    762px 1926px #ce93d8,\n    1202px 1424px #ce93d8,\n    135px 1316px #ce93d8,\n    1442px 1692px #ce93d8,\n    977px 652px #ce93d8,\n    564px 1648px #ce93d8,\n    997px 1474px #ce93d8,\n    67px 1366px #ce93d8,\n    1860px 1451px #ce93d8,\n    1105px 772px #ce93d8,\n    1886px 1396px #ce93d8,\n    1510px 658px #ce93d8,\n    976px 1544px #ce93d8,\n    894px 543px #ce93d8,\n    1098px 1189px #ce93d8,\n    690px 77px #ce93d8,\n    770px 733px #ce93d8,\n    557px 1403px #ce93d8,\n    1758px 1623px #ce93d8,\n    1341px 812px #ce93d8,\n    699px 967px #ce93d8,\n    277px 866px #ce93d8,\n    1526px 1828px #ce93d8,\n    8px 977px #ce93d8,\n    1707px 952px #ce93d8,\n    12px 1900px #ce93d8,\n    72px 921px #ce93d8,\n    496px 1067px #ce93d8,\n    1288px 1749px #ce93d8,\n    273px 984px #ce93d8,\n    1197px 1991px #ce93d8,\n    242px 789px #ce93d8,\n    903px 1035px #ce93d8,\n    480px 1492px #ce93d8,\n    102px 1331px #ce93d8,\n    738px 1343px #ce93d8,\n    560px 1475px #ce93d8,\n    367px 846px #ce93d8,\n    1420px 962px #ce93d8,\n    1976px 892px #ce93d8,\n    1911px 1763px #ce93d8,\n    1639px 1002px #ce93d8,\n    437px 1522px #ce93d8,\n    1906px 1025px #ce93d8,\n    730px 1364px #ce93d8,\n    1127px 521px #ce93d8,\n    1401px 1792px #ce93d8,\n    1954px 1066px #ce93d8,\n    232px 250px #ce93d8,\n    1685px 660px #ce93d8,\n    1011px 999px #ce93d8,\n    1970px 790px #ce93d8,\n    750px 499px #ce93d8,\n    1738px 660px #ce93d8,\n    1621px 1849px #ce93d8,\n    446px 52px #ce93d8,\n    1055px 1396px #ce93d8,\n    1165px 1497px #ce93d8,\n    1740px 1425px #ce93d8,\n    1012px 1920px #ce93d8,\n    1258px 1560px #ce93d8,\n    1020px 1152px #ce93d8,\n    362px 673px #ce93d8,\n    1065px 975px #ce93d8,\n    582px 755px #ce93d8,\n    1271px 1479px #ce93d8,\n    719px 548px #ce93d8,\n    1602px 879px #ce93d8,\n    590px 499px #ce93d8,\n    721px 1412px #ce93d8,\n    1180px 113px #ce93d8,\n    1801px 1961px #ce93d8,\n    589px 941px #ce93d8,\n    883px 476px #ce93d8,\n    214px 890px #ce93d8,\n    1028px 892px #ce93d8,\n    1107px 1832px #ce93d8,\n    944px 361px #ce93d8,\n    480px 1453px #ce93d8,\n    1466px 683px #ce93d8,\n    981px 745px #ce93d8,\n    1968px 828px #ce93d8,\n    657px 1830px #ce93d8,\n    11px 1338px #ce93d8,\n    179px 730px #ce93d8,\n    1713px 197px #ce93d8,\n    51px 955px #ce93d8,\n    1243px 319px #ce93d8,\n    1175px 624px #ce93d8,\n    446px 46px #ce93d8,\n    5px 1158px #ce93d8,\n    82px 1352px #ce93d8,\n    1877px 402px #ce93d8,\n    708px 1778px #ce93d8,\n    903px 1625px #ce93d8,\n    1824px 352px #ce93d8,\n    1229px 140px #ce93d8,\n    1518px 24px #ce93d8,\n    1017px 512px #ce93d8,\n    515px 699px #ce93d8,\n    295px 265px #ce93d8,\n    69px 1773px #ce93d8,\n    1640px 1163px #ce93d8,\n    536px 342px #ce93d8,\n    970px 1766px #ce93d8,\n    560px 1416px #ce93d8,\n    577px 193px #ce93d8,\n    469px 9px #ce93d8,\n    466px 276px #ce93d8,\n    711px 853px #ce93d8,\n    401px 685px #ce93d8,\n    85px 506px #ce93d8,\n    865px 558px #ce93d8,\n    631px 105px #ce93d8,\n    887px 866px #ce93d8,\n    1704px 1001px #ce93d8,\n    1051px 1199px #ce93d8,\n    275px 1909px #ce93d8,\n    1462px 829px #ce93d8,\n    375px 1057px #ce93d8,\n    1531px 1501px #ce93d8,\n    205px 403px #ce93d8,\n    33px 1869px #ce93d8,\n    967px 1176px #ce93d8,\n    376px 863px #ce93d8,\n    1769px 1545px #ce93d8,\n    535px 51px #ce93d8,\n    1972px 1569px #ce93d8,\n    1773px 960px #ce93d8,\n    487px 620px #ce93d8,\n    1660px 687px #ce93d8,\n    1632px 972px #ce93d8,\n    1362px 42px #ce93d8,\n    479px 1655px #ce93d8,\n    1531px 1808px #ce93d8,\n    1450px 1412px #ce93d8,\n    1549px 170px #ce93d8,\n    1904px 1305px #ce93d8,\n    1209px 48px #ce93d8,\n    1933px 820px #ce93d8,\n    1623px 595px #ce93d8,\n    48px 643px #ce93d8,\n    179px 1754px #ce93d8,\n    589px 1032px #ce93d8,\n    1199px 356px #ce93d8,\n    1755px 1418px #ce93d8,\n    780px 1174px #ce93d8,\n    1905px 758px #ce93d8,\n    1567px 713px #ce93d8,\n    1372px 705px #ce93d8,\n    456px 654px #ce93d8,\n    759px 690px #ce93d8,\n    452px 673px #ce93d8,\n    993px 1610px #ce93d8,\n    1271px 188px #ce93d8,\n    343px 1750px #ce93d8,\n    1943px 1735px #ce93d8,\n    1717px 853px #ce93d8,\n    1247px 303px #ce93d8,\n    1314px 1895px #ce93d8,\n    1203px 489px #ce93d8,\n    741px 469px #ce93d8,\n    4px 246px #ce93d8,\n    1515px 115px #ce93d8,\n    606px 218px #ce93d8,\n    1966px 1471px #ce93d8,\n    177px 87px #ce93d8,\n    1575px 588px #ce93d8,\n    1136px 1386px #ce93d8,\n    70px 1868px #ce93d8,\n    1053px 18px #ce93d8,\n    1124px 721px #ce93d8,\n    1748px 1181px #ce93d8,\n    191px 1387px #ce93d8,\n    1931px 840px #ce93d8,\n    1088px 1603px #ce93d8,\n    634px 1255px #ce93d8,\n    814px 1434px #ce93d8,\n    585px 64px #ce93d8,\n    1074px 1618px #ce93d8,\n    1692px 761px #ce93d8,\n    651px 643px #ce93d8,\n    193px 335px #ce93d8,\n    1103px 1447px #ce93d8,\n    491px 1142px #ce93d8,\n    521px 408px #ce93d8,\n    536px 340px #ce93d8,\n    411px 1091px #ce93d8,\n    1646px 193px #ce93d8,\n    1595px 1285px #ce93d8,\n    870px 1349px #ce93d8,\n    1085px 1013px #ce93d8,\n    204px 1864px #ce93d8,\n    1359px 299px #ce93d8,\n    807px 964px #ce93d8,\n    219px 509px #ce93d8,\n    36px 1227px #ce93d8,\n    702px 1873px #ce93d8,\n    1471px 934px #ce93d8,\n    1763px 792px #ce93d8,\n    973px 1957px #ce93d8,\n    987px 68px #ce93d8,\n    593px 1282px #ce93d8,\n    1900px 607px #ce93d8,\n    407px 1659px #ce93d8,\n    587px 17px #ce93d8,\n    632px 158px #ce93d8;\n}\n\n@keyframes animStar {\n  from {\n    transform: translateY(0px);\n  }\n\n  to {\n    transform: translateY(-2000px);\n  }\n}\n"
  },
  {
    "path": "docs/pages/concepts/_meta.js",
    "content": "export default {\n  index: \"Overview\",\n  oauth: \"How OAuth works\",\n  \"session-strategies\": \"Session strategies\",\n  \"database-models\": \"Database models\",\n}\n"
  },
  {
    "path": "docs/pages/concepts/database-models.mdx",
    "content": "import { Callout } from \"nextra/components\"\n\n# Database models\n\nAuth.js can be used with any database. Models tell you what structures Auth.js expects from your database. Models will vary slightly depending on which adapter you use, but in general, will have a similar structure to the graph below. Each model can be extended with additional fields.\n\n<Callout type=\"info\">\n  Auth.js uses `camelCase` for its database rows while respecting the\n  conventional `snake_case` formatting for OAuth-related values. If the mixed\n  casing is an issue for you, most adapters have a dedicated documentation\n  section on how to force a casing convention.\n</Callout>\n\n```mermaid\nerDiagram\n    User ||--|{ Account : \"\"\n    User {\n      string id\n      string name\n      string email\n      timestamp emailVerified\n      string image\n    }\n    User ||--|{ Session : \"\"\n    Session {\n      string id\n      timestamp expires\n      string sessionToken\n      string userId\n    }\n    Account {\n      string id\n      string userId\n      string type\n      string provider\n      string providerAccountId\n      string refresh_token\n      string access_token\n      int expires_at\n      string token_type\n      string scope\n      string id_token\n      string session_state\n    }\n    User ||--|{ VerificationToken : \"\"\n    VerificationToken {\n      string identifier\n      string token\n      timestamp expires\n    }\n```\n\n---\n\n### User\n\nThe User model is for information such as the user's name and email address. Email address is optional, but if one is specified for a `User`, then it must be unique.\n\n<Callout>\n  User creation in the database is automatic and happens when the user is logged\n  in for the first time with an authentication provider (either OAuth, magic\n  links or plain credentials).\n</Callout>\n\n**OAuth sign in**\n\nIf the first sign-in is via the [OAuth Provider](/getting-started/authentication/oauth), the default data\nsaved is `id`, `name`, `email` and `image`. You can add more profile data by returning extra\nfields in your [OAuth provider](/guides/configuring-oauth-providers#use-your-own-provider)'s [`profile()`](/reference/core/providers#profile) callback.\n\n**Magic links sign in**\n\nIf the first sign-in is via the [Email Provider](/getting-started/authentication/email), then the saved user will have `id`, `email`, `emailVerified`, where `emailVerified` is the timestamp of when the user was created.\n\n### Account\n\nThe Account model is for information about accounts associated with a `User`. A single `User` can have multiple `Account`(s), but each `Account` can only have one `User`.\n\n<Callout>\n  Account creation in the database is automatic and happens when the user is\n  logged in for the first time with an authentication provider (either OAuth,\n  magic links or plain credentials) or the\n  [`Adapter.linkAccount`](/reference/core/adapters#linkaccount) method is\n  invoked.\n</Callout>\n\nThe default data saved is `access_token`, `expires_at`, `refresh_token`, `id_token`, `token_type`,\n`scope` and `session_state`. You can save other fields or remove the ones you don't need by\nreturning them in the [OAuth provider](/guides/configuring-oauth-providers#use-your-own-provider)'s [`account()`](/reference/core/providers#account) callback.\n\nLinking `Accounts`(s) to `User`(s) happen automatically, only when they have the same e-mail address, and the user is currently signed in. Check the [FAQ](/concepts#security) for more information on why this is a requirement.\n\nYou can manually unlink accounts if your adapter implements the\n`unlinkAccount` method. Make sure to take all the necessary security steps to\navoid data loss.\n\n### Session\n\nEven if you are using a database, you can still use **JWT** for session handling for fast access, in which case, this model can be opted out in your database.\n\n- [Learn more about session strategies](/concepts/session-strategies) and their trade-offs.\n\nThe Session model is used for database sessions and it can store arbitrary data for an active user session. A single `User` can have multiple `Session`(s), each `Session` can only have one `User`.\n\nWhen a Session is read, its `expires` field is checked to see if the session is still valid. If it has expired, the session is deleted from the database. You can also do this clean-up periodically in the background to avoid Auth.js\nextra delete call to the database during an active session retrieval. This\nmight result in a slight performance increase.\n\n### VerificationToken\n\nThe `VerificationToken` model is used to store tokens for email-based **magic-link** sign in.\n\nA single `User` can have multiple open `VerificationToken`s active (e.g. sign in with different devices).\n\n<Callout>\n  Due to users forgetting or failures during the sign-in flow, you might end up\n  with unwanted rows in your database. You might want to periodically clean\n  these up to avoid filling up your database with unnecessary data.\n</Callout>\n\nIt has been designed so that it can be extended for other verification purposes in the future (e.g. 2FA / magic codes, etc... ).\n\nAuth.js makes sure that every token is usable only once, and by default has a short lifetime (1 day, can be configured by `maxAge`). If your user did not manage to finish the sign-in flow in time, they will have to start the sign-in process again.\n"
  },
  {
    "path": "docs/pages/concepts/index.mdx",
    "content": "import { Accordion, Accordions } from \"@/components/Accordion\"\n\n# About Auth.js\n\n### Is Auth.js commercial software?\n\nAuth.js is an open-source project built by individual contributors.\n\nIt is not commercial software and is not associated with a commercial organization.\n\n### Compatibility\n\n<Accordions>\n<Accordion title=\"What databases does Auth.js support?\">\n\nYou can use Auth.js with many flavors of MySQL, MariaDB, PostgreSQL, MongoDB and SQLite. See our [using a database adapter guide](/getting-started/database). There are adapters for most popular database types available.\n\nYou can use also Auth.js with any database using a custom database adapter, or by using a custom credentials authentication provider - e.g. to support signing in with a username and password stored in an existing database.\n\nYou do not need a database to use Auth.js, however. You can use Auth.js with JWT-based sessions, which do not require a database.\n\n</Accordion>\n\n<Accordion title=\"What authentication services does Auth.js support?\">\n\nAuth.js includes built-in support for 4 high-level authentication types - OAuth / OICD, Email magic-links, WebAuthn / Passkeys and username/password credentials. Check out our [authentication page](/getting-started/authentication) for more information.\n\n</Accordion>\n\n<Accordion title=\"Does Auth.js support signing in with a username and password?\">\n\nAuth.js is designed to avoid the need to store passwords and user accounts.\n\nHowever, if you'd still like to use username/password based login, then you can use our [Credentials provider](/getting-started/authentication/credentials) to allow signing in with a username and password.\n\n_If you use a custom credentials provider user accounts will not be persisted in a database by Auth.js (even if one is configured). The option to use JSON Web Tokens for session tokens (which allow sign-in without using a session database) must be enabled to use a custom credentials provider._\n\n</Accordion>\n\n<Accordion title=\"Can I use Auth.js with a website that does not use Next.js?\">\n\nAuth.js was initially designed for use with Next.js and Serverless, however, over time we've evolved into a framework-agnostic authentication solution. If you're curious, take a look at [our history page](/contributors#history). We currently have framework packages available for Next.js (`next-auth`), SvelteKit (`@auth/sveltekit`), SolidStart (`@auth/solid`), Express (`@auth/express`) and more in the pipeline.\n\n</Accordion>\n\n<Accordion title=\"Can I use Auth.js with React Native?\">\n\nAuth.js is designed as a secure, confidential client and implements a server-side authentication flow.\n\nIt is not intended to be used in native applications on desktop or mobile applications, which typically implement public clients (e.g. with client/secrets embedded in the application).\n\n</Accordion>\n\n<Accordion title=\"Is Auth.js supporting TypeScript?\">\n\nYes! Check out the [TypeScript docs](/getting-started/typescript)\n\n</Accordion>\n\n<Accordion title=\"Is Auth.js compatible with Next.js Proxy / Middleware?\">\n\nYes! As of Next.js 16, `middleware.ts` has been renamed to `proxy.ts`. Auth.js supports both the new `proxy.ts` convention and the legacy `middleware.ts`. An example setup can be found [here](/getting-started/session-management/protecting#nextjs-proxy).\n\n</Accordion>\n</Accordions>\n\n### Session strategies\n\nCheck out the [Session strategies page](/concepts/session-strategies) to learn more.\n\n### Security\n\nParts of this section have been moved to their own [page](/security).\n\n<Accordions>\n<Accordion title=\"How do I get Refresh Tokens and Access Tokens for an OAuth account?\">\n\nAuth.js provides a solution for authentication, session management and user account creation.\n\nAuth.js records Refresh Tokens and Access Tokens on sign-in (if supplied by the provider) and it will pass them, along with the User ID, Provider and Provider Account ID, to either:\n\n1. A database - if a database connection string is provided\n2. The JSON Web Token callback - if JWT sessions are enabled (e.g. if no database is specified)\n\nYou can then look them up from the database or persist them to the JSON Web Token.\n\nNote: Auth.js does not currently handle Access Token rotation for OAuth providers for you, however, you can check out [this tutorial](/guides/refresh-token-rotation) if you want to implement it.\n\n</Accordion>\n<Accordion title=\"When I sign in with another account with the same email address, why are accounts not linked automatically?\">\n\nAutomatic account linking on sign-in is not secure between arbitrary providers - except for allowing users to sign in via email addresses as a fallback (as they must verify their email address as part of the flow).\n\nWhen an email address is associated with an OAuth account it does not necessarily mean that it has been verified as belonging to the account holder — how email address verification is handled is not part of the OAuth specification and varies between providers (e.g. some do not verify first, some do verify first, others return metadata indicating the verification status).\n\nWith automatic account linking on sign-in, this can be exploited by bad parties to hijack accounts by creating an OAuth account associated with the email address of another user.\n\nFor this reason, it is not secure to automatically link accounts between arbitrary providers on sign-in, which is why this feature is generally not provided by an authentication service and is not provided by Auth.js.\n\nAutomatic account linking is seen on some sites, sometimes insecurely. It can be technically possible to do automatic account linking securely if you trust all the providers involved to ensure they have securely verified the email address associated with the account, but requires placing trust (and transferring the risk) to those providers to handle the process securely.\n\nExamples of scenarios where this is secure include an OAuth provider you control (e.g. that only authorizes users internal to your organization) or a provider you explicitly trust to have verified the users' email address.\n\nAutomatic account linking is not a planned feature of Auth.js, however, there is scope to improve the user experience of account linking and of handling this flow, securely. Typically this involves providing a fallback option to sign in via email, which is already possible (and recommended), but the current implementation of this flow could be improved.\n\nProviding support for secure account linking and unlinking of additional providers - which can only be done if a user is already signed in - was originally a feature in v1.x but has not been present since v2.0, and is planned to return in a future release.\n\n</Accordion>\n</Accordions>\n\n### Feature Requests\n\n<Accordions>\n<Accordion title=\"Why doesn't Auth.js support [a particular feature]?\">\n\nAuth.js is an open-source project built by individual contributors who are volunteers writing code and providing support in their spare time.\n\nIf you would like Auth.js to support a particular feature, the best way to help make it happen is to raise a feature request describing the feature and offer to work with other contributors to develop and test it.\n\n</Accordion>\n<Accordion title=\"I disagree with a design decision, how can I change your mind?\">\n\nProduct design decisions on Auth.js are made by core team members.\n\nYou can raise suggestions as feature requests for enhancement.\n\nRequests that provide the detail requested in the template and follow the format requested may be more likely to be supported, as additional detail prompted in the templates often provides important context.\n\nUltimately if your request is not accepted or is not actively in development, you are always free to fork the project under the terms of the ISC License.\n\n</Accordion>\n</Accordions>\n"
  },
  {
    "path": "docs/pages/concepts/oauth.mdx",
    "content": "---\ntitle: Auth.js | OAuth\n---\n\nimport { Callout } from \"nextra/components\"\nimport { Screenshot } from \"@/components/Screenshot\"\n\n# OAuth\n\n<Callout>\n  Auth.js is designed to work with any OAuth service, it supports **OAuth 2.0**\n  and **OpenID Connect** and has built-in support for most popular sign-in\n  services.\n</Callout>\n\nAuthentication Providers in **Auth.js** are predefined [OAuth](https://oauth.net/2/) configurations that allow your users to sign in with pre-existing logins at their favorite services. You can use any of our predefined providers, or write your own custom OAuth configuration. For customizing or writing your own OAuth provider, see our [configuring OAuth providers](/guides/configuring-oauth-providers) guide.\n\nAt a high level, the OAuth **Authorization Code** flow we support generally has 6 parts:\n\n1. The application requests authorization to access service resources from the user\n2. If the user authorized the request, the application receives an authorization grant\n3. The application requests an access token from the authorization server (API) by presenting authentication of its own identity, and the authorization grant\n4. If the application identity is authenticated and the authorization grant is valid, the authorization server (API) issues an access token to the application. Authorization is complete.\n5. The application requests the resource from the resource server (API) and presents the access token for authentication\n6. If the access token is valid, the resource server (API) serves the resource to the application\n\n## Diagrams\n\nBelow are two diagrams visually illustrating the same basic flow as described above, the OAuth Authorization Code flow. First is a sequence diagram.\n\n```mermaid\nsequenceDiagram\n    participant Browser\n    participant App Server\n    participant Auth Server (GitHub)\n    Note left of Browser: User clicks on \"Sign in\"\n    Browser->>App Server: GET<br/>\"api/auth/signin\"\n    App Server->>App Server: Computes the available<br/>sign in providers<br/>from the \"providers\" option\n    App Server->>Browser: Redirects to Sign in page\n    Note left of Browser: Sign in options<br/>are shown the user<br/>(GitHub, Twitter, etc...)\n    Note left of Browser: User clicks on<br/>\"Sign in with GitHub\"\n    Browser->>App Server: POST<br/>\"api/auth/signin/github\"\n    App Server->>App Server: Computes sign in<br/>options for GitHub<br/>(scopes, callback URL, etc...)\n    App Server->>Auth Server (GitHub): GET<br/>\"github.com/login/oauth/authorize\"\n    Note left of Auth Server (GitHub): Sign in options<br> are supplied as<br/>query params<br/>(clientId, <br/>scope, etc...)\n    Auth Server (GitHub)->>Browser: Shows sign in page<br/>in GitHub.com<br/>to the user\n    Note left of Browser: User inserts their<br/>credentials in GitHub\n    Browser->>Auth Server (GitHub): GitHub validates the inserted credentials\n    Auth Server (GitHub)->>Auth Server (GitHub): Generates one time access code<br/>and calls callback<br>URL defined in<br/>App settings\n    Auth Server (GitHub)->>App Server: GET<br/>\"api/auth/github/callback?code=123\"\n    App Server->>App Server: Grabs code<br/>to exchange it for<br/>access token\n    App Server->>Auth Server (GitHub): POST<br/>\"github.com/login/oauth/access_token\"<br/>{code: 123}\n    Auth Server (GitHub)->>Auth Server (GitHub): Verifies code is<br/>valid and generates<br/>access token\n    Auth Server (GitHub)->>App Server: { access_token: 16C7x... }\n    App Server->>App Server: Generates session token<br/>and stores session\n    App Server->>Browser: You're now logged in!\n```\n\nNext is a swim lane diagram which comes from a great article, [Setting up OAuth with Auth.js and SvelteKit](https://mainmatter.com/blog/2023/11/23/setting-up-oauth-with-auth-js-and-sveltekit/) by [Andrey Mikhaylov](https://lolma.us) of [mainmatter.com](https://mainmatter.com).\n\nimport OAuthDiagram from \"../../public/img/concepts/oauth-diagram.webp\"\n\n<Screenshot src={OAuthDiagram} alt=\"OAuth Flow Diagram\" />\n\n## Further Reading\n\nTo learn more, check out the following blog posts:\n\n- Aaron Parecki's blog post [OAuth2 Simplified](https://aaronparecki.com/oauth-2-simplified/)\n- Postman's blog post [OAuth 2.0: Implicit Flow is Dead, Try PKCE Instead](https://blog.postman.com/pkce-oauth-how-to/)\n- Setting up OAuth with Auth.js and SvelteKit [blog post](https://mainmatter.com/blog/2023/11/23/setting-up-oauth-with-auth-js-and-sveltekit/)\n"
  },
  {
    "path": "docs/pages/concepts/session-strategies.mdx",
    "content": "import { Callout } from \"nextra/components\"\n\n# Session strategies\n\nWhen a user visits your application, you want them to not have to log in every single time. After logging in once, your application should save some data about the user and allow them to pick up where they left off the next time they visit. This is called a session. **Auth.js supports 2 main session strategies**, the JWT-based session and Database session.\n\n<Callout>\n  Both session strategies have advantages and disadvantages, which you have to\n  evaluate based on the requirements of your application.\n</Callout>\n\nYou can configure the session strategy using the [`session.strategy`](/reference/core#strategy) option in the main Auth.js config file.\n\n## JWT Session\n\nAuth.js can create sessions using [JSON Web Tokens (JWT)](https://datatracker.ietf.org/doc/html/rfc7519). This is the default session strategy for Auth.js unless a database provider is configured. When a user signs in, a JWT is created [in a `HttpOnly` cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#block_access_to_your_cookies). Making the cookie `HttpOnly` prevents JavaScript from accessing it client-side (via `document.cookie`, for example), which makes it harder for attackers to steal the value. In addition, the JWT is encrypted with a secret key only known to the server. So, even if an attacker were to steal the JWT from the cookie, they could not decrypt it. Combined with a short expiration time, this makes JWTs a secure way to create sessions.\n\nWhen a user signs out, Auth.js deletes the JWT from the cookies, destroying the session.\n\n### Advantages\n\n- JWTs as a session do not require a database to store sessions; this can be faster, cheaper, and easier to scale.\n- This strategy requires fewer resources as you don't need to manage an extra database/service.\n- You may then use the created token to pass information between services and APIs on the same domain without having to contact a database to verify the included information.\n- You can use JWT to securely store information without exposing it to third-party JavaScript running on your site.\n\n### Disadvantages\n\n- Expiring a JSON Web Token before its encoded expiry is not possible - doing so requires maintaining a server-side blocklist of invalidated tokens (at least until they truly expire) and checking every token against the list every time a token is presented. Auth.js **will** destroy the cookie, but if the user has the JWT saved elsewhere, it will be valid (the server will accept it) until it expires. (Shorter session expiry times are used when using JSON Web Tokens as session tokens to allow sessions to be invalidated sooner and simplify this problem.)\n- Auth.js enables advanced features to mitigate the downsides of using shorter session expiry times on the user experience, including automatic session token rotation, optionally sending keep-alive messages (session polling) to prevent short-lived sessions from expiring if there is a window or tab open, background re-validation, and automatic tab/window syncing that keeps sessions in sync across windows any time session state changes or a window or tab gains or loses focus.\n- As with database session tokens, JSON Web Tokens are limited in the amount of data you can store in them. There is typically a limit of around 4096 bytes per cookie, though the exact limit varies between browsers. The more data you try to store in a token and the more other cookies you set, the closer you will come to this limit. Auth.js implements session cookie chunking so that cookies over the 4kb limit will get split and reassembled upon parsing. However, since this data needs to be transmitted on every request, you need to be aware of how much data you want to transfer using this technique.\n- Even if appropriately configured, information stored in an encrypted JWT should not be assumed to be impossible to decrypt at some point - e.g., due to the discovery of a defect or advances in technology. Data stored in an encrypted JSON Web Token (JWE) _may_ be compromised at some point. The recommendation is to generate a [secret](/reference/core#secret) with high entropy.\n\n## Database session\n\nAlternatively to a JWT session strategy, Auth.js also supports database sessions. In this case, instead of saving a JWT with user data after signing in, Auth.js will create a session in your database. A session ID is then saved in a `HttpOnly` cookie. This is similar to the JWT session strategy, but instead of saving the user data in the cookie, it only stores an obscure value pointing to the session in the database. So whenever you try to access the user session, you will query the database for the data.\n\nWhen a user signs out, the session is deleted from the database, and the session ID is deleted from the cookies.\n\n### Advantages\n\n- Database sessions can be at any time modified server-side, so you can implement features that might be more difficult - but not impossible - using the JWT strategy, etc.: \"sign out everywhere\", or limiting concurrent logins\n- Auth.js has no opinion on the type of database you are using; we have a big list of [official\n  database adapters](/getting-started/database), but you can [implement your\n  own](/guides/creating-a-database-adapter) as well\n\n### Disadvantages\n\n- Database sessions need a roundtrip to your database, so they might be slower at scale unless your connections/databases are accommodated for it\n- Many database adapters are not yet compatible with the Edge, which would allow faster and cheaper session retrieval\n- Setting up a database takes more effort and requires extra services to manage compared to the stateless JWT strategy\n"
  },
  {
    "path": "docs/pages/contributors.mdx",
    "content": "import { Steps } from \"nextra/components\"\n\n# Contributors\n\nMaintaining Auth.js as an open source project is very hard work. All the core team members have regular jobs and the library is maintained and developed out of good will in our free time. Donations can enable the core team to eventually work full time on Auth.js to provide more features and an even better developer experience!\n\n<div className=\"my-4 flex gap-2\">\n  <img src=\"/img/etc/opencollective-logomark.svg\" width=\"64\" />\n  <img src=\"/img/etc/opencollective-wordmark.svg\" width=\"250\" />\n</div>\n\nYou can find us on [Open Collective](https://opencollective.com/nextauth). We are very thankful for all of our existing contributors and would be delighted if you or your company would decide to join them.\n\n## Core team\n\nWithout these people, the project could not have become one of the most used authentication libraries in its category.\n\n- [Balázs Orbán](https://github.com/balazsorban44) - Lead Maintainer\n- [Thang Vu](https://github.com/ThangHuuVu) - Maintainer\n- [Nico Domino](https://github.com/ndom91) - Maintainer\n- [Lluis Agusti](https://github.com/lluia) - Maintainer\n\n## Special thanks\n\nSpecial thanks to Filip Skokan for their feedback and high-quality OAuth libraries that we build on, Lori Karikari for creating most of the original provider configurations, Fredrik Pettersen for creating the original Prisma Adapter, Gerald Nolan for adding support for Sign in with Apple, Jefferson Bledsoe for working on original testing automation, and Tom Grey for their work/guidance on the API Reference documentation.\n\n- [Filip Skokan](https://github.com/panva)\n- [Lori Karikari](https://github.com/LoriKarikari)\n- [Fredrik Pettersen](https://github.com/Fumler)\n- [Gerald Nolan](https://github.com/geraldnolan)\n- [Jefferson Bledsoe](https://github.com/JeffersonBledsoe)\n- [Tom Grey](https://github.com/tgreyuk)\n\n## Other contributors\n\nAuth.js as it exists today has been possible thanks to the work of many individual contributors.\n\nThank you to the [dozens of individual contributors](https://github.com/nextauthjs/next-auth/graphs/contributors) who have helped shape Auth.js.\n\n## History\n\n<Steps>\n\n### 2016 – Initial release\n\nNextAuth.js was originally developed by [Iain Collins](https://github.com/iaincollins) in 2016 as an authentication framework specific to [Next.js](https://nextjs.org/).\n\n### 2020 – Refactor and clean up\n\nNextAuth.js was rebuilt from the ground up to support serverless, MySQL, Postgres, MongoDB, JSON Web Tokens and built-in support for over a dozen authentication providers.\n\n[Balázs Orbán](https://github.com/balazsorban44) joined as a co-maintainer, helping to offload some of the work from Iain.\n\n### 2021 – Multi-framework effort\n\nIain and Balázs defined future goals for the project. Their vision aligned perfectly, discussing that NextAuth.js could one day become useful for other frameworks.\n\nIain left the project to focus on other things, knowing that Balázs will work on their shared vision.\n\nBalázs became the lead maintainer of the project.\n\nEfforts started to move NextAuth.js to other frameworks and to support as many databases and providers as possible.\n\nIt was shown that a single package could not support all of these use cases.\n\nDatabase Adapters were moved to their packages under the name `@next-auth/*-adapter`.\n\n### 2022 – Birth of Auth.js\n\nBased on NextAuth.js, Balázs released [Auth.js (`@auth/core`)](https://twitter.com/balazsorban44/status/1603082914362986496), a runtime/framework independent core library that is the base of\nall Auth.js libraries. A complete rewrite that still shared most of the public API with NextAuth.js but internally was very different.\n\n### 2023 – Auth.js silent releases\n\nDue to personal reasons, Balázs had to step down as the lead maintainer, but was still contributing. The project was taken over by [Thang Huu Vu](https://github.com/thanghuuvu) for a while.\n\nBalázs returned and continued the work on Auth.js. As a pilot project, `next-auth@experimental` (later `next-auth@beta`) releases were published\nto work out what was needed in the core library to support other frameworks and what was framework specific from the old NextAuth.js implementation.\n\nThe new default documentation page became [authjs.dev](https://authjs.dev) (the one you are reading right now), and the old NextAuth.js documentation at [next-auth.js.org](https://next-auth.js.org) was kept around\nto document NextAuth.js v4 and is kept only as a back reference.\n\nDatabase Adapters were moved from the `@next-auth/*-adapter` namespace to `@auth/*-adapter`, indicating that they are not NextAuth.js specific anymore.\n\nCommunity integrations started showing up, making it clear that the initial vision of Auth.js was shared by many.\n\n### 2024 – Growing the Auth.js ecosystem\n\nWith the release of [NextAuth.js v5](/getting-started/migrating-to-v5), now all Auth.js libraries are based on the same core library.\n**The name \"NextAuth.js\" designates only the Next.js integration**, while Auth.js refers to the core library and our ecosystem as a whole.\nOther integrations will generally be refered to with their framework name + Auth, e.g. \"SvelteKit Auth\" or \"Express Auth\".\n\nAll official integrations are distributed under the `@auth` scope except for NextAuth.js, which is distributed under `next-auth` to reduce migration overhead.\n\n</Steps>\n\n## Notes\n\nThe Auth.js/NextAuth.js project is not provided by, nor otherwise affiliated with Vercel Inc. or its subsidiaries. Any contributions to this project by individuals affiliated with Vercel are made in their personal capacity.\n"
  },
  {
    "path": "docs/pages/data/manifest.json",
    "content": "{\n  \"frameworks\": [\n    {\n      \"id\": \"Next.js\",\n      \"name\": \"Auth.js\",\n      \"url\": \"next-auth-example\"\n    },\n    {\n      \"id\": \"sveltekit\",\n      \"name\": \"SvelteKit Auth\",\n      \"url\": \"sveltekit-auth-example\"\n    },\n    {\n      \"id\": \"solidstart\",\n      \"name\": \"SolidStart Auth\",\n      \"url\": \"auth-solid\"\n    }\n  ],\n  \"adapters\": {\n    \"prisma\": \"Prisma\",\n    \"drizzle\": \"Drizzle ORM\",\n    \"neon\": \"Neon\",\n    \"supabase\": \"Supabase\",\n    \"firebase\": \"Firebase\",\n    \"typeorm\": \"TypeORM\",\n    \"kysely\": \"Kysely\",\n    \"upstash-redis\": \"Upstash Redis\",\n    \"azure-tables\": \"Azure Tables Storage\",\n    \"d1\": \"D1\",\n    \"dgraph\": \"Dgraph\",\n    \"dynamodb\": \"DynamoDB\",\n    \"edgedb\": \"EdgeDB\",\n    \"fauna\": \"Fauna\",\n    \"hasura\": \"Hasura\",\n    \"mikro-orm\": \"Mikro ORM\",\n    \"mongodb\": \"MongoDB\",\n    \"neo4j\": \"Neo4j\",\n    \"pg\": \"pg\",\n    \"pouchdb\": \"PouchDB\",\n    \"sequelize\": \"Sequelize\",\n    \"surrealdb\": \"SurrealDB\",\n    \"unstorage\": \"Unstorage\",\n    \"xata\": \"Xata\"\n  },\n  \"providersOAuth\": {\n    \"github\": \"GitHub\",\n    \"google\": \"Google\",\n    \"twitter\": \"Twitter\",\n    \"apple\": \"Apple\",\n    \"discord\": \"Discord\",\n    \"auth0\": \"Auth0\",\n    \"facebook\": \"Facebook\",\n    \"keycloak\": \"Keycloak\",\n    \"42-school\": \"42 School\",\n    \"asgardeo\": \"Asgardeo\",\n    \"atlassian\": \"Atlassian\",\n    \"authentik\": \"Authentik\",\n    \"azure-ad-b2c\": \"Azure Active Directory B2C\",\n    \"azure-ad\": \"Azure Active Directory\",\n    \"azure-devops\": \"Azure DevOps\",\n    \"battlenet\": \"Battle.net\",\n    \"beyondidentity\": \"Beyond Identity\",\n    \"bitbucket\": \"Bitbucket\",\n    \"box\": \"Box\",\n    \"boxyhq-saml\": \"BoxyHQ SAML\",\n    \"bungie\": \"Bungie\",\n    \"click-up\": \"ClickUp\",\n    \"cognito\": \"Cognito\",\n    \"coinbase\": \"Coinbase\",\n    \"descope\": \"Descope\",\n    \"dribbble\": \"Dribbble\",\n    \"dropbox\": \"Dropbox\",\n    \"duende-identityserver-6\": \"DuendeIdentityServer6\",\n    \"eveonline\": \"EVE Online\",\n    \"faceit\": \"FACEIT\",\n    \"figma\": \"Figma\",\n    \"foursquare\": \"Foursquare\",\n    \"freshbooks\": \"Freshbooks\",\n    \"fusionauth\": \"FusionAuth\",\n    \"gitlab\": \"GitLab\",\n    \"hubspot\": \"HubSpot\",\n    \"huggingface\": \"Hugging Face\",\n    \"identity-server4\": \"IdentityServer4\",\n    \"instagram\": \"Instagram\",\n    \"kakao\": \"Kakao\",\n    \"kinde\": \"Kinde\",\n    \"line\": \"LINE\",\n    \"linkedin\": \"LinkedIn\",\n    \"logto\": \"Logto\",\n    \"mailchimp\": \"Mailchimp\",\n    \"mailru\": \"Mail.ru\",\n    \"mastodon\": \"Mastodon\",\n    \"mattermost\": \"Mattermost\",\n    \"medium\": \"Medium\",\n    \"microsoft-entra-id\": \"Microsoft Entra ID\",\n    \"naver\": \"Naver\",\n    \"netlify\": \"Netlify\",\n    \"notion\": \"Notion\",\n    \"okta\": \"Okta\",\n    \"onelogin\": \"OneLogin\",\n    \"osso\": \"Osso\",\n    \"osu\": \"Osu!\",\n    \"passage\": \"Passage\",\n    \"patreon\": \"Patreon\",\n    \"pinterest\": \"Pinterest\",\n    \"pipedrive\": \"Pipedrive\",\n    \"reddit\": \"Reddit\",\n    \"salesforce\": \"Salesforce\",\n    \"slack\": \"Slack\",\n    \"spotify\": \"Spotify\",\n    \"strava\": \"Strava\",\n    \"tiktok\": \"TikTok\",\n    \"todoist\": \"Todoist\",\n    \"trakt\": \"Trakt\",\n    \"twitch\": \"Twitch\",\n    \"united-effects\": \"United Effects\",\n    \"vk\": \"VK\",\n    \"webex\": \"Webex\",\n    \"wikimedia\": \"Wikimedia\",\n    \"wordpress\": \"WordPress.com\",\n    \"workos\": \"WorkOS\",\n    \"yandex\": \"Yandex\",\n    \"zitadel\": \"ZITADEL\",\n    \"zoho\": \"Zoho\",\n    \"zoom\": \"Zoom\"\n  },\n  \"providersEmail\": {\n    \"forwardemail\": \"Forward Email\",\n    \"resend\": \"Resend\",\n    \"sendgrid\": \"Sendgrid\",\n    \"nodemailer\": \"Nodemailer\"\n  },\n  \"requiresIssuer\": [\n    \"asgardeo\",\n    \"auth0\",\n    \"authentik\",\n    \"azure-ad-b2c\",\n    \"battlenet\",\n    \"beyondidentity\",\n    \"boxyhq-saml\",\n    \"cognito\",\n    \"descope\",\n    \"duende-identityserver-6\",\n    \"fusionauth\",\n    \"identity-server4\",\n    \"keycloak\",\n    \"kinde\",\n    \"logto\",\n    \"mastodon\",\n    \"mattermost\",\n    \"microsoft-entra-id\",\n    \"nextcloud\",\n    \"okta\",\n    \"ory-hydra\",\n    \"osso\",\n    \"passage\",\n    \"ping-id\"\n  ]\n}\n"
  },
  {
    "path": "docs/pages/getting-started/_meta.js",
    "content": "export default {\n  index: {\n    title: \"Introduction\",\n    theme: {\n      typesetting: \"article\",\n    },\n  },\n  \"migrate-to-better-auth\": {\n    title: \"Migrate to Better Auth\",\n  },\n  \"-- getting-started\": {\n    type: \"separator\",\n    title: \"Getting Started\",\n  },\n  installation: \"Installation\",\n  authentication: {\n    title: \"Authentication\",\n    theme: {\n      toc: false,\n    },\n  },\n  database: \"Database\",\n  \"session-management\": {\n    title: \"Session Management\",\n    theme: {\n      toc: false,\n    },\n  },\n  deployment: \"Deployment\",\n  typescript: \"TypeScript\",\n  \"-- connect\": {\n    type: \"separator\",\n    title: \"Connections\",\n  },\n  providers: \"Providers\",\n  adapters: \"Adapters\",\n  integrations: {\n    title: \"Integrations\",\n    theme: {\n      typesetting: \"article\",\n    },\n  },\n}\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/_meta.js",
    "content": "export default {\n  \"azure-tables\": \"Azure Tables\",\n  d1: \"Cloudflare D1\",\n  dgraph: \"Dgraph\",\n  drizzle: \"Drizzle\",\n  dynamodb: \"DynamoDB\",\n  edgedb: \"EdgeDB\",\n  fauna: \"FaunaDB\",\n  firebase: \"Firebase Firestore\",\n  hasura: \"Hasura\",\n  kysely: \"Kysely\",\n  \"mikro-orm\": \"MikroORM\",\n  mongodb: \"MongoDB\",\n  neo4j: \"Neo4j\",\n  neon: \"Neon\",\n  pg: \"PostgreSQL\",\n  pouchdb: \"PouchDB\",\n  prisma: \"Prisma\",\n  sequelize: \"Sequelize\",\n  supabase: \"Supabase\",\n  surrealdb: \"SurrealDB\",\n  typeorm: \"TypeORM\",\n  unstorage: \"Unstorage\",\n  \"upstash-redis\": \"Upstash Redis\",\n  xata: \"Xata\",\n}\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/azure-tables.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n<img\n  align=\"right\"\n  src=\"/img/adapters/azure-tables.svg\"\n  width=\"64\"\n  height=\"64\"\n/>\n\n# Azure Table Storage Adapter\n\n## Resources\n\n- [Azure Tables documentation](https://azure.microsoft.com/en-us/products/storage/tables)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/azure-tables-adapter\n```\n\n### Environment Variables\n\n```\nAUTH_AZURE_ACCOUNT=storageaccountname\nAUTH_AZURE_ACCESS_KEY=longRandomKey\nAUTH_AZURE_TABLES_ENDPOINT=https://$AZURE_ACCOUNT.table.core.windows.net\n```\n\n### Configuration\n\n1. Create a table for authentication data, `auth` in the example below.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth, { type AuthConfig } from \"next-auth\"\nimport { TableStorageAdapter } from \"@auth/azure-tables-adapter\"\nimport { AzureNamedKeyCredential, TableClient } from \"@azure/data-tables\"\n\nconst credential = new AzureNamedKeyCredential(\n  process.env.AUTH_AZURE_ACCOUNT,\n  process.env.AUTH_AZURE_ACCESS_KEY\n)\nconst authClient = new TableClient(\n  process.env.AUTH_AZURE_TABLES_ENDPOINT,\n  \"auth\",\n  credential\n)\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [],\n  adapter: TableStorageAdapter(authClient),\n} satisfies AuthConfig)\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { TableStorageAdapter } from \"@auth/azure-tables-adapter\"\nimport { AzureNamedKeyCredential, TableClient } from \"@azure/data-tables\"\n\nconst credential = new AzureNamedKeyCredential(\n  import.meta.env.AUTH_AZURE_ACCOUNT,\n  import.meta.env.AUTH_AZURE_ACCESS_KEY\n)\nconst authClient = new TableClient(\n  import.meta.env.AUTH_AZURE_TABLES_ENDPOINT,\n  \"auth\",\n  credential\n)\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: TableStorageAdapter(authClient),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport SvelteKitAuth, { type AuthConfig } from \"@auth/sveltekit\"\nimport { TableStorageAdapter } from \"@auth/azure-tables-adapter\"\nimport { AzureNamedKeyCredential, TableClient } from \"@azure/data-tables\"\n\nconst credential = new AzureNamedKeyCredential(\n  process.env.AUTH_AZURE_ACCOUNT,\n  process.env.AUTH_AZURE_ACCESS_KEY\n)\nconst authClient = new TableClient(\n  process.env.AUTH_AZURE_TABLES_ENDPOINT,\n  \"auth\",\n  credential\n)\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [],\n  adapter: TableStorageAdapter(authClient),\n} satisfies AuthConfig)\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport express from \"express\"\nimport Google from \"@auth/express/providers/google\"\nimport ExpressAuth, { type AuthConfig } from \"@auth/express\"\nimport { TableStorageAdapter } from \"@auth/azure-tables-adapter\"\nimport { AzureNamedKeyCredential, TableClient } from \"@azure/data-tables\"\n\nconst app = express()\n\nconst credential = new AzureNamedKeyCredential(\n  process.env.AUTH_AZURE_ACCOUNT,\n  process.env.AUTH_AZURE_ACCESS_KEY\n)\nconst authClient = new TableClient(\n  process.env.AUTH_AZURE_TABLES_ENDPOINT,\n  \"auth\",\n  credential\n)\n\n// If app is served through a proxy, trust the proxy to allow HTTPS protocol to be detected\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [Google],\n    adapter: TableStorageAdapter(authClient),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/d1.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/d1.svg\" width=\"64\" height=\"64\" />\n\n# Cloudflare D1 Adapter\n\n## Resources\n\n- [Cloudflare D1 documentation](https://developers.cloudflare.com/d1/)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install next-auth @auth/d1-adapter\n```\n\n### Environment Variables\n\nEnvironment variables in Cloudflare's platform are set either via a [`wrangler.toml`](https://developers.cloudflare.com/workers/wrangler/configuration/#environment-variables) configuration file, or in the [admin dashboard](https://dash.cloudflare.com/?to=/:account/pages/view/:pages-project/settings/environment-variables).\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { D1Adapter } from \"@auth/d1-adapter\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [],\n  adapter: D1Adapter(env.db),\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { D1Adapter } from \"@auth/d1-adapter\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: D1Adapter(env.db),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { D1Adapter } from \"@auth/d1-adapter\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [],\n  adapter: D1Adapter(env.db),\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { D1Adapter } from \"@auth/d1-adapter\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: D1Adapter(env.db),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Migrations\n\nSomewhere in the initialization of your application you need to run the `up(env.db)` function to create the tables in D1.\nIt will create 4 tables if they don't already exist:\n`accounts`, `sessions`, `users`, `verification_tokens`.\n\nThe table prefix `\"\"` is not configurable at this time.\n\nYou can use something like the following to attempt the migration once each time your worker starts up. Running migrations more than once will not erase your existing tables.\n\n```javascript\nimport { up } from \"@auth/d1-adapter\"\n\nlet migrated = false\nasync function migrationHandle({ event, resolve }) {\n  if (!migrated) {\n    try {\n      await up(event.platform.env.db)\n      migrated = true\n    } catch (e) {\n      console.log(e.cause.message, e.message)\n    }\n  }\n  return resolve(event)\n}\n```\n\n- You can also initialize your tables manually. Look in [migrations.ts](https://github.com/nextauthjs/next-auth/blob/main/packages/adapter-d1/src/migrations.ts) for the relevant SQL as well as an example of the `up()` function from above.\n- Paste and execute the SQL from within your D1 database's console in the [Cloudflare dashboard](https://dash.cloudflare.com/?to=/:account/workers/d1).\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/dgraph.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/dgraph.svg\" width=\"64\" height=\"64\" />\n\n# Dgraph Adapter\n\n## Resources\n\n- [Dgraph documentation](https://dgraph.io/docs)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/dgraph-adapter\n```\n\n### Environment Variables\n\n```sh\nAUTH_DGRAPH_GRAPHQL_ENDPOINT=http://localhost:8080/graphql\nAUTH_DGRAPH_GRAPHQL_KEY=abc123\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { DgraphAdapter } from \"@auth/dgraph-adapter\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [],\n  adapter: DgraphAdapter({\n    endpoint: process.env.AUTH_DGRAPH_GRAPHQL_ENDPOINT,\n    authToken: process.env.AUTH_DGRAPH_GRAPHQL_KEY,\n    // you can omit the following properties if you are running an unsecure schema\n    authHeader: process.env.AUTH_HEADER, // default: \"Authorization\",\n    jwtSecret: process.env.AUTH_SECRET,\n  }),\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { DgraphAdapter } from \"@auth/dgraph-adapter\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: DgraphAdapter({\n      endpoint: import.meta.env.DGRAPH_GRAPHQL_ENDPOINT,\n      authToken: import.meta.env.DGRAPH_GRAPHQL_KEY,\n      // you can omit the following properties if you are running an unsecure schema\n      authHeader: import.meta.env.AUTH_HEADER, // default: \"Authorization\",\n      jwtSecret: import.meta.env.SECRET,\n    }),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { DgraphAdapter } from \"@auth/dgraph-adapter\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [],\n  adapter: DgraphAdapter({\n    endpoint: process.env.DGRAPH_GRAPHQL_ENDPOINT,\n    authToken: process.env.DGRAPH_GRAPHQL_KEY,\n    // you can omit the following properties if you are running an unsecure schema\n    authHeader: process.env.AUTH_HEADER, // default: \"Authorization\",\n    jwtSecret: process.env.SECRET,\n  }),\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { DgraphAdapter } from \"@auth/dgraph-adapter\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: DgraphAdapter({\n      endpoint: process.env.DGRAPH_GRAPHQL_ENDPOINT,\n      authToken: process.env.DGRAPH_GRAPHQL_KEY,\n      // you can omit the following properties if you are running an unsecure schema\n      authHeader: process.env.AUTH_HEADER, // default: \"Authorization\",\n      jwtSecret: process.env.SECRET,\n    }),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Unsecure Schema\n\nThe quickest way to use Dgraph is by applying the unsecure schema to your [local](https://dgraph.io/docs/graphql/admin/#modifying-a-schema) Dgraph instance or if using Dgraph [cloud](https://dgraph.io/docs/cloud/cloud-quick-start/#the-schema) you can paste the schema in the codebox to update.\n\n<Callout type=\"warning\">\n  This approach is not secure or for production use, and does not require a\n  `jwtSecret`.\n</Callout>\n\n> This schema is adapted for use in Dgraph and based upon our main [schema](https://authjs.dev/reference/core/adapters)\n\n#### Example\n\n```graphql\ntype Account {\n  id: ID\n  type: String\n  provider: String @search(by: [hash])\n  providerAccountId: String @search(by: [hash])\n  refreshToken: String\n  expires_at: Int64\n  accessToken: String\n  token_type: String\n  refresh_token: String\n  access_token: String\n  scope: String\n  id_token: String\n  session_state: String\n  user: User @hasInverse(field: \"accounts\")\n}\ntype Session {\n  id: ID\n  expires: DateTime\n  sessionToken: String @search(by: [hash])\n  user: User @hasInverse(field: \"sessions\")\n}\ntype User {\n  id: ID\n  name: String\n  email: String @search(by: [hash])\n  emailVerified: DateTime\n  image: String\n  accounts: [Account] @hasInverse(field: \"user\")\n  sessions: [Session] @hasInverse(field: \"user\")\n}\n\ntype VerificationToken {\n  id: ID\n  identifier: String @search(by: [hash])\n  token: String @search(by: [hash])\n  expires: DateTime\n}\n```\n\n### Secure schema\n\nFor production deployments you will want to restrict the access to the types used\nby next-auth. The main form of access control used in Dgraph is via `@auth` directive alongside types in the schema.\n\n#### Example\n\n```graphql\ntype Account\n  @auth(\n    delete: { rule: \"{$nextAuth: { eq: true } }\" }\n    add: { rule: \"{$nextAuth: { eq: true } }\" }\n    query: { rule: \"{$nextAuth: { eq: true } }\" }\n    update: { rule: \"{$nextAuth: { eq: true } }\" }\n  ) {\n  id: ID\n  type: String\n  provider: String @search(by: [hash])\n  providerAccountId: String @search(by: [hash])\n  refreshToken: String\n  expires_at: Int64\n  accessToken: String\n  token_type: String\n  refresh_token: String\n  access_token: String\n  scope: String\n  id_token: String\n  session_state: String\n  user: User @hasInverse(field: \"accounts\")\n}\ntype Session\n  @auth(\n    delete: { rule: \"{$nextAuth: { eq: true } }\" }\n    add: { rule: \"{$nextAuth: { eq: true } }\" }\n    query: { rule: \"{$nextAuth: { eq: true } }\" }\n    update: { rule: \"{$nextAuth: { eq: true } }\" }\n  ) {\n  id: ID\n  expires: DateTime\n  sessionToken: String @search(by: [hash])\n  user: User @hasInverse(field: \"sessions\")\n}\ntype User\n  @auth(\n    query: {\n      or: [\n        {\n          rule: \"\"\"\n          query ($userId: String!) {queryUser(filter: { id: { eq: $userId } } ) {id}}\n          \"\"\"\n        }\n        { rule: \"{$nextAuth: { eq: true } }\" }\n      ]\n    }\n    delete: { rule: \"{$nextAuth: { eq: true } }\" }\n    add: { rule: \"{$nextAuth: { eq: true } }\" }\n    update: {\n      or: [\n        {\n          rule: \"\"\"\n          query ($userId: String!) {queryUser(filter: { id: { eq: $userId } } ) {id}}\n          \"\"\"\n        }\n        { rule: \"{$nextAuth: { eq: true } }\" }\n      ]\n    }\n  ) {\n  id: ID\n  name: String\n  email: String @search(by: [hash])\n  emailVerified: DateTime\n  image: String\n  accounts: [Account] @hasInverse(field: \"user\")\n  sessions: [Session] @hasInverse(field: \"user\")\n}\n\ntype VerificationToken\n  @auth(\n    delete: { rule: \"{$nextAuth: { eq: true } }\" }\n    add: { rule: \"{$nextAuth: { eq: true } }\" }\n    query: { rule: \"{$nextAuth: { eq: true } }\" }\n    update: { rule: \"{$nextAuth: { eq: true } }\" }\n  ) {\n  id: ID\n  identifier: String @search(by: [hash])\n  token: String @search(by: [hash])\n  expires: DateTime\n}\n\n# Dgraph.Authorization {\"VerificationKey\":\"<YOUR JWT SECRET HERE>\",\"Header\":\"<YOUR AUTH HEADER HERE>\",\"Namespace\":\"<YOUR CUSTOM NAMESPACE HERE>\",\"Algo\":\"HS256\"}\n```\n\n### Dgraph.Authorization\n\nIn order to secure your graphql backend define the `Dgraph.Authorization` object at the\nbottom of your schema and provide `authHeader` and `jwtSecret` values to the DgraphClient.\n\n```js\n# Dgraph.Authorization {\"VerificationKey\":\"<YOUR JWT SECRET HERE>\",\"Header\":\"<YOUR AUTH HEADER HERE>\",\"Namespace\":\"YOUR CUSTOM NAMESPACE HERE\",\"Algo\":\"HS256\"}\n```\n\n### VerificationKey and jwtSecret\n\nThis is the key used to sign the JWT. Ex. `process.env.SECRET` or `process.env.APP_SECRET`.\n\n### Header and authHeader\n\nThe `Header` tells Dgraph where to lookup a JWT within the headers of the incoming requests made to the dgraph server.\nYou have to configure it at the bottom of your schema file. This header is the same as the `authHeader` property you\nprovide when you instantiate the `DgraphClient`.\n\n### The nextAuth secret\n\nThe `$nextAuth` secret is securely generated using the `jwtSecret` and injected by the DgraphAdapter in order to allow interacting with the JWT DgraphClient for anonymous user requests made within the system `ie. login, register`. This allows\nsecure interactions to be made with all the auth types required by next-auth. You have to specify it for each auth rule of\neach type defined in your secure schema.\n\n```js\ntype VerificationRequest\n  @auth(\n    delete: { rule: \"{$nextAuth: { eq: true } }\" },\n    add: { rule: \"{$nextAuth: { eq: true } }\" },\n    query: { rule: \"{$nextAuth: { eq: true } }\" },\n    update: { rule: \"{$nextAuth: { eq: true } }\" }\n  ) {\n}\n```\n\n### JWT session and `@auth` directive\n\nDgraph only works with HS256 or RS256 algorithms. If you want to use session jwt to securely interact with your dgraph\ndatabase you must customize next-auth `encode` and `decode` functions, as the default algorithm is HS512. You can\nfurther customize the jwt with roles if you want to implement [`RBAC logic`](https://dgraph.io/docs/graphql/authorization/directive/#role-based-access-control).\n\n```js filename=\"./auth.js\"\nimport NextAuth from \"next-auth\"\nimport * as jwt from \"jsonwebtoken\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  session: {\n    strategy: \"jwt\",\n  },\n  jwt: {\n    secret: process.env.SECRET,\n    encode: async ({ secret, token }) => {\n      return jwt.sign({ ...token, userId: token.id }, secret, {\n        algorithm: \"HS256\",\n        expiresIn: 30 * 24 * 60 * 60, // 30 days\n      })\n    },\n    decode: async ({ secret, token }) => {\n      return jwt.verify(token, secret, { algorithms: [\"HS256\"] })\n    },\n  },\n})\n```\n\nOnce your `Dgraph.Authorization` is defined in your schema and the JWT settings are set, this will allow you to define [`@auth rules`](https://dgraph.io/docs/graphql/authorization/authorization-overview/) for every part of your schema.\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/drizzle.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\nimport { Accordion, Accordions } from \"@/components/Accordion\"\n\n<a href=\"https://orm.drizzle.team\">\n  <img align=\"right\" src=\"/img/adapters/drizzle.svg\" width=\"64\" height=\"64\" />\n</a>\n\n# Drizzle ORM Adapter\n\n## Resources\n\n- [Drizzle ORM documentation](https://orm.drizzle.team/docs/overview)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install drizzle-orm @auth/drizzle-adapter\nnpm install drizzle-kit --save-dev\n```\n\n### Environment Variables\n\n```sh\nAUTH_DRIZZLE_URL=postgres://postgres:postgres@127.0.0.1:5432/db\n```\n\n### Configuration\n\nTo use this adapter, you must have setup Drizzle ORM and Drizzle Kit in your project. Drizzle provides a simple [quick start guide](https://orm.drizzle.team/kit-docs/quick). For more details, follow the Drizzle documentation for your respective database ([PostgreSQL](https://orm.drizzle.team/docs/get-started-postgresql), [MySQL](https://orm.drizzle.team/docs/get-started-mysql) or [SQLite](https://orm.drizzle.team/docs/get-started-sqlite)). In summary, that setup should look something like this.\n\n1. Create your schema file, based off of one of the ones below.\n2. Install a supported database driver to your project, like `@libsql/client`, `mysql2` or `postgres`.\n3. Create a `drizzle.config.ts` [file](https://orm.drizzle.team/kit-docs/conf).\n4. Generate the initial migration from your schema file with a command like, `drizzle-kit generate`.\n5. Apply migrations by using `migrate()` function or push changes directly to your database with a command like, `drizzle-kit push`.\n6. If your schemas differ from the default ones, pass them as the second parameter to the adapter.\n\n#### Schemas\n\n<Accordions>\n<Accordion title=\"PostgreSQL\">\nIf you want to modify the schema or add additional fields, you can use the following schema as a starting point:\n\n```ts filename=\"schema.ts\"\nimport {\n  boolean,\n  timestamp,\n  pgTable,\n  text,\n  primaryKey,\n  integer,\n} from \"drizzle-orm/pg-core\"\nimport postgres from \"postgres\"\nimport { drizzle } from \"drizzle-orm/postgres-js\"\nimport type { AdapterAccountType } from \"@auth/core/adapters\"\n\nconst connectionString = \"postgres://postgres:postgres@localhost:5432/drizzle\"\nconst pool = postgres(connectionString, { max: 1 })\n\nexport const db = drizzle(pool)\n\nexport const users = pgTable(\"user\", {\n  id: text(\"id\")\n    .primaryKey()\n    .$defaultFn(() => crypto.randomUUID()),\n  name: text(\"name\"),\n  email: text(\"email\").unique(),\n  emailVerified: timestamp(\"emailVerified\", { mode: \"date\" }),\n  image: text(\"image\"),\n})\n\nexport const accounts = pgTable(\n  \"account\",\n  {\n    userId: text(\"userId\")\n      .notNull()\n      .references(() => users.id, { onDelete: \"cascade\" }),\n    type: text(\"type\").$type<AdapterAccountType>().notNull(),\n    provider: text(\"provider\").notNull(),\n    providerAccountId: text(\"providerAccountId\").notNull(),\n    refresh_token: text(\"refresh_token\"),\n    access_token: text(\"access_token\"),\n    expires_at: integer(\"expires_at\"),\n    token_type: text(\"token_type\"),\n    scope: text(\"scope\"),\n    id_token: text(\"id_token\"),\n    session_state: text(\"session_state\"),\n  },\n  (account) => [\n    {\n      compoundKey: primaryKey({\n        columns: [account.provider, account.providerAccountId],\n      }),\n    },\n  ]\n)\n\nexport const sessions = pgTable(\"session\", {\n  sessionToken: text(\"sessionToken\").primaryKey(),\n  userId: text(\"userId\")\n    .notNull()\n    .references(() => users.id, { onDelete: \"cascade\" }),\n  expires: timestamp(\"expires\", { mode: \"date\" }).notNull(),\n})\n\nexport const verificationTokens = pgTable(\n  \"verificationToken\",\n  {\n    identifier: text(\"identifier\").notNull(),\n    token: text(\"token\").notNull(),\n    expires: timestamp(\"expires\", { mode: \"date\" }).notNull(),\n  },\n  (verificationToken) => [\n    {\n      compositePk: primaryKey({\n        columns: [verificationToken.identifier, verificationToken.token],\n      }),\n    },\n  ]\n)\n\nexport const authenticators = pgTable(\n  \"authenticator\",\n  {\n    credentialID: text(\"credentialID\").notNull().unique(),\n    userId: text(\"userId\")\n      .notNull()\n      .references(() => users.id, { onDelete: \"cascade\" }),\n    providerAccountId: text(\"providerAccountId\").notNull(),\n    credentialPublicKey: text(\"credentialPublicKey\").notNull(),\n    counter: integer(\"counter\").notNull(),\n    credentialDeviceType: text(\"credentialDeviceType\").notNull(),\n    credentialBackedUp: boolean(\"credentialBackedUp\").notNull(),\n    transports: text(\"transports\"),\n  },\n  (authenticator) => [\n    {\n      compositePK: primaryKey({\n        columns: [authenticator.userId, authenticator.credentialID],\n      }),\n    },\n  ]\n)\n```\n\n</Accordion>\n<Accordion title=\"MySQL\">\nIf you want to modify the schema or add additional fields, you can use the following schema as a starting point:\n\n```ts filename=\"schema.ts\"\nimport {\n  boolean,\n  int,\n  timestamp,\n  mysqlTable,\n  primaryKey,\n  varchar,\n} from \"drizzle-orm/mysql-core\"\nimport mysql from \"mysql2/promise\"\nimport { drizzle } from \"drizzle-orm/mysql2\"\nimport type { AdapterAccountType } from \"next-auth/adapters\"\n\nexport const connection = await mysql.createConnection({\n  host: \"host\",\n  user: \"user\",\n  password: \"password\",\n  database: \"database\",\n})\n\nexport const db = drizzle(connection)\n\nexport const users = mysqlTable(\"user\", {\n  id: varchar(\"id\", { length: 255 })\n    .primaryKey()\n    .$defaultFn(() => crypto.randomUUID()),\n  name: varchar(\"name\", { length: 255 }),\n  email: varchar(\"email\", { length: 255 }).unique(),\n  emailVerified: timestamp(\"emailVerified\", {\n    mode: \"date\",\n    fsp: 3,\n  }),\n  image: varchar(\"image\", { length: 255 }),\n})\n\nexport const accounts = mysqlTable(\n  \"account\",\n  {\n    userId: varchar(\"userId\", { length: 255 })\n      .notNull()\n      .references(() => users.id, { onDelete: \"cascade\" }),\n    type: varchar(\"type\", { length: 255 })\n      .$type<AdapterAccountType>()\n      .notNull(),\n    provider: varchar(\"provider\", { length: 255 }).notNull(),\n    providerAccountId: varchar(\"providerAccountId\", { length: 255 }).notNull(),\n    refresh_token: varchar(\"refresh_token\", { length: 255 }),\n    access_token: varchar(\"access_token\", { length: 255 }),\n    expires_at: int(\"expires_at\"),\n    token_type: varchar(\"token_type\", { length: 255 }),\n    scope: varchar(\"scope\", { length: 255 }),\n    id_token: varchar(\"id_token\", { length: 2048 }),\n    session_state: varchar(\"session_state\", { length: 255 }),\n  },\n  (account) => ({\n    compoundKey: primaryKey({\n      columns: [account.provider, account.providerAccountId],\n    }),\n  })\n)\n\nexport const sessions = mysqlTable(\"session\", {\n  sessionToken: varchar(\"sessionToken\", { length: 255 }).primaryKey(),\n  userId: varchar(\"userId\", { length: 255 })\n    .notNull()\n    .references(() => users.id, { onDelete: \"cascade\" }),\n  expires: timestamp(\"expires\", { mode: \"date\" }).notNull(),\n})\n\nexport const verificationTokens = mysqlTable(\n  \"verificationToken\",\n  {\n    identifier: varchar(\"identifier\", { length: 255 }).notNull(),\n    token: varchar(\"token\", { length: 255 }).notNull(),\n    expires: timestamp(\"expires\", { mode: \"date\" }).notNull(),\n  },\n  (verificationToken) => ({\n    compositePk: primaryKey({\n      columns: [verificationToken.identifier, verificationToken.token],\n    }),\n  })\n)\n\nexport const authenticators = mysqlTable(\n  \"authenticator\",\n  {\n    credentialID: varchar(\"credentialID\", { length: 255 }).notNull().unique(),\n    userId: varchar(\"userId\", { length: 255 })\n      .notNull()\n      .references(() => users.id, { onDelete: \"cascade\" }),\n    providerAccountId: varchar(\"providerAccountId\", { length: 255 }).notNull(),\n    credentialPublicKey: varchar(\"credentialPublicKey\", {\n      length: 255,\n    }).notNull(),\n    counter: int(\"counter\").notNull(),\n    credentialDeviceType: varchar(\"credentialDeviceType\", {\n      length: 255,\n    }).notNull(),\n    credentialBackedUp: boolean(\"credentialBackedUp\").notNull(),\n    transports: varchar(\"transports\", { length: 255 }),\n  },\n  (authenticator) => ({\n    compositePk: primaryKey({\n      columns: [authenticator.userId, authenticator.credentialID],\n    }),\n  })\n)\n```\n\n</Accordion>\n<Accordion title=\"SQLite\">\nIf you want to modify the schema or add additional fields, you can use the following schema as a starting point:\n\n```ts filename=\"schema.ts\"\nimport { integer, sqliteTable, text, primaryKey } from \"drizzle-orm/sqlite-core\"\nimport { createClient } from \"@libsql/client\"\nimport { drizzle } from \"drizzle-orm/libsql\"\nimport type { AdapterAccountType } from \"next-auth/adapters\"\n\nconst client = createClient({\n  url: \"DATABASE_URL\",\n  authToken: \"DATABASE_AUTH_TOKEN\",\n})\nexport const db = drizzle(client)\n\nexport const users = sqliteTable(\"user\", {\n  id: text(\"id\")\n    .primaryKey()\n    .$defaultFn(() => crypto.randomUUID()),\n  name: text(\"name\"),\n  email: text(\"email\").unique(),\n  emailVerified: integer(\"emailVerified\", { mode: \"timestamp_ms\" }),\n  image: text(\"image\"),\n})\n\nexport const accounts = sqliteTable(\n  \"account\",\n  {\n    userId: text(\"userId\")\n      .notNull()\n      .references(() => users.id, { onDelete: \"cascade\" }),\n    type: text(\"type\").$type<AdapterAccountType>().notNull(),\n    provider: text(\"provider\").notNull(),\n    providerAccountId: text(\"providerAccountId\").notNull(),\n    refresh_token: text(\"refresh_token\"),\n    access_token: text(\"access_token\"),\n    expires_at: integer(\"expires_at\"),\n    token_type: text(\"token_type\"),\n    scope: text(\"scope\"),\n    id_token: text(\"id_token\"),\n    session_state: text(\"session_state\"),\n  },\n  (account) => [\n    primaryKey({\n      columns: [account.provider, account.providerAccountId],\n    }),\n  ]\n)\n\nexport const sessions = sqliteTable(\"session\", {\n  sessionToken: text(\"sessionToken\").primaryKey(),\n  userId: text(\"userId\")\n    .notNull()\n    .references(() => users.id, { onDelete: \"cascade\" }),\n  expires: integer(\"expires\", { mode: \"timestamp_ms\" }).notNull(),\n})\n\nexport const verificationTokens = sqliteTable(\n  \"verificationToken\",\n  {\n    identifier: text(\"identifier\").notNull(),\n    token: text(\"token\").notNull(),\n    expires: integer(\"expires\", { mode: \"timestamp_ms\" }).notNull(),\n  },\n  (verificationToken) => [\n    primaryKey({\n      columns: [verificationToken.identifier, verificationToken.token],\n    }),\n  ]\n)\n\nexport const authenticators = sqliteTable(\n  \"authenticator\",\n  {\n    credentialID: text(\"credentialID\").notNull().unique(),\n    userId: text(\"userId\")\n      .notNull()\n      .references(() => users.id, { onDelete: \"cascade\" }),\n    providerAccountId: text(\"providerAccountId\").notNull(),\n    credentialPublicKey: text(\"credentialPublicKey\").notNull(),\n    counter: integer(\"counter\").notNull(),\n    credentialDeviceType: text(\"credentialDeviceType\").notNull(),\n    credentialBackedUp: integer(\"credentialBackedUp\", {\n      mode: \"boolean\",\n    }).notNull(),\n    transports: text(\"transports\"),\n  },\n  (authenticator) => [\n    primaryKey({\n      columns: [authenticator.userId, authenticator.credentialID],\n    }),\n  ]\n)\n```\n\n</Accordion>\n</Accordions>\n\n### Adapter Setup\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { DrizzleAdapter } from \"@auth/drizzle-adapter\"\nimport { db } from \"./schema.ts\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: DrizzleAdapter(db),\n  providers: [],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { DrizzleAdapter } from \"@auth/drizzle-adapter\"\nimport { db } from \"./schema.ts\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: DrizzleAdapter(db),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { DrizzleAdapter } from \"@auth/drizzle-adapter\"\nimport { db } from \"./schema.ts\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: DrizzleAdapter(db),\n  providers: [],\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { DrizzleAdapter } from \"@auth/drizzle-adapter\"\nimport { db } from \"./schema.ts\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: DrizzleAdapter(db),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n#### Passing your own Schemas\n\nIf you want to use your own tables, you can pass them as a second argument to `DrizzleAdapter`.\n\n- The `sessionsTable` is optional and only required if you're using the database session strategy.\n- The `verificationTokensTable` is optional and only required if you're using a Magic Link provider.\n\n```ts filename=\"auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Google from \"next-auth/providers/google\"\nimport { DrizzleAdapter } from \"@auth/drizzle-adapter\"\nimport { db, accounts, sessions, users, verificationTokens } from \"./schema\"\n\nexport const { handlers, auth } = NextAuth({\n  adapter: DrizzleAdapter(db, {\n    usersTable: users,\n    accountsTable: accounts,\n    sessionsTable: sessions,\n    verificationTokensTable: verificationTokens,\n  }),\n  providers: [Google],\n})\n```\n\n### Migrating your database\n\nWith your schema now described in your code, you'll need to migrate your database to your schema. An example `migrate.ts` file looks like this. For more information, check out Drizzle's migration [quick start guide](https://orm.drizzle.team/docs/migrations).\n\n```ts filename=\"migrate.ts\"\nimport \"dotenv/config\"\nimport { migrate } from \"drizzle-orm/mysql2/migrator\"\nimport { db, connection } from \"./db\"\n\n// This will run migrations on the database, skipping the ones already applied\nawait migrate(db, { migrationsFolder: \"./drizzle\" })\n\n// Don't forget to close the connection, otherwise the script will hang\nawait connection.end()\n```\n\nFull documentation on how to manage migrations with Drizzle can be found at the Drizzle Kit [Migrations page](https://orm.drizzle.team/kit-docs/overview#running-migrations).\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/dynamodb.mdx",
    "content": "---\ntitle: DynamoDB\n---\n\nimport { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\nimport { Accordion, Accordions } from \"@/components/Accordion\"\n\n<img align=\"right\" src=\"/img/adapters/dynamodb.svg\" width=\"64\" height=\"64\" />\n\n# DynamoDB Adapter\n\n## Resources\n\n- [DynamoDB documentation](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/dynamodb-adapter @aws-sdk/lib-dynamodb @aws-sdk/client-dynamodb\n```\n\n### Environment Variables\n\n```sh\nAUTH_DYNAMODB_ID=accessKey\nAUTH_DYNAMODB_SECRET=secretKey\nAUTH_DYNAMODB_REGION=eu-west-1\n```\n\n### Configuration\n\nYou need to pass `DynamoDBDocument` client from the modular [`aws-sdk`](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/dynamodb-example-dynamodb-utilities.html) v3 to the adapter.\nThe default table name is `next-auth`, but you can customise that by passing `{ tableName: 'your-table-name' }` as the second parameter in the adapter.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { DynamoDB, DynamoDBClientConfig } from \"@aws-sdk/client-dynamodb\"\nimport { DynamoDBDocument } from \"@aws-sdk/lib-dynamodb\"\nimport { DynamoDBAdapter } from \"@auth/dynamodb-adapter\"\n\nconst config: DynamoDBClientConfig = {\n  credentials: {\n    accessKeyId: process.env.AUTH_DYNAMODB_ID,\n    secretAccessKey: process.env.AUTH_DYNAMODB_SECRET,\n  },\n  region: process.env.AUTH_DYNAMODB_REGION,\n}\n\nconst client = DynamoDBDocument.from(new DynamoDB(config), {\n  marshallOptions: {\n    convertEmptyValues: true,\n    removeUndefinedValues: true,\n    convertClassInstanceToMap: true,\n  },\n})\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: []\n  adapter: DynamoDBAdapter(client),\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { DynamoDB, DynamoDBClientConfig } from \"@aws-sdk/client-dynamodb\"\nimport { DynamoDBDocument } from \"@aws-sdk/lib-dynamodb\"\nimport { DynamoDBAdapter } from \"@auth/dynamodb-adapter\"\n\nconst config: DynamoDBClientConfig = {\n  credentials: {\n    accessKeyId: import.meta.env.AUTH_DYNAMODB_ID,\n    secretAccessKey: import.meta.env.AUTH_DYNAMODB_SECRET,\n  },\n  region: import.meta.env.AUTH_DYNAMODB_REGION,\n}\n\nconst client = DynamoDBDocument.from(new DynamoDB(config), {\n  marshallOptions: {\n    convertEmptyValues: true,\n    removeUndefinedValues: true,\n    convertClassInstanceToMap: true,\n  },\n})\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: DynamoDBAdapter(client),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { DynamoDB, DynamoDBClientConfig } from \"@aws-sdk/client-dynamodb\"\nimport { DynamoDBDocument } from \"@aws-sdk/lib-dynamodb\"\nimport { DynamoDBAdapter } from \"@auth/dynamodb-adapter\"\n\nconst config: DynamoDBClientConfig = {\n  credentials: {\n    accessKeyId: process.env.AUTH_DYNAMODB_ID,\n    secretAccessKey: process.env.AUTH_DYNAMODB_SECRET,\n  },\n  region: process.env.AUTH_DYNAMODB_REGION,\n}\n\nconst client = DynamoDBDocument.from(new DynamoDB(config), {\n  marshallOptions: {\n    convertEmptyValues: true,\n    removeUndefinedValues: true,\n    convertClassInstanceToMap: true,\n  },\n})\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: []\n  adapter: DynamoDBAdapter(client),\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { DynamoDB, DynamoDBClientConfig } from \"@aws-sdk/client-dynamodb\"\nimport { DynamoDBDocument } from \"@aws-sdk/lib-dynamodb\"\nimport { DynamoDBAdapter } from \"@auth/dynamodb-adapter\"\n\nconst app = express()\n\nconst config: DynamoDBClientConfig = {\n  credentials: {\n    accessKeyId: process.env.AUTH_DYNAMODB_ID,\n    secretAccessKey: process.env.AUTH_DYNAMODB_SECRET,\n  },\n  region: process.env.AUTH_DYNAMODB_REGION,\n}\n\nconst client = DynamoDBDocument.from(new DynamoDB(config), {\n  marshallOptions: {\n    convertEmptyValues: true,\n    removeUndefinedValues: true,\n    convertClassInstanceToMap: true,\n  },\n})\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: DynamoDBAdapter(client),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### AWS Credentials\n\n<Callout type=\"info\">\n  Always follow the **principle of least privilege** when giving access to AWS\n  services/resources -> identities should only be permitted to perform the\n  smallest set of actions necessary to fulfill a specific task.\n</Callout>\n\n1. Open the [AWS console](https://console.aws.amazon.com/) and go to \"IAM\", then \"Users\".\n2. Create a new user. The purpose of this user is to give programmatic access to DynamoDB.\n3. Create an Access Key and then copy Key ID and Secret to your `.env`/`.env.local` file.\n4. Select \"Add Permission\" and \"Create Inline Policy\".\n5. Copy the JSON below into the JSON input and replace `region`, `account_id` and `table_name` with your values.\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"DynamoDBAccess\",\n      \"Effect\": \"Allow\",\n      \"Action\": [\n        \"dynamodb:BatchGetItem\",\n        \"dynamodb:BatchWriteItem\",\n        \"dynamodb:Describe*\",\n        \"dynamodb:List*\",\n        \"dynamodb:PutItem\",\n        \"dynamodb:DeleteItem\",\n        \"dynamodb:GetItem\",\n        \"dynamodb:Scan\",\n        \"dynamodb:Query\",\n        \"dynamodb:UpdateItem\"\n      ],\n      \"Resource\": [\n        \"arn:aws:dynamodb:{region}:{account_id}:table/{table_name}\",\n        \"arn:aws:dynamodb:{region}:{account_id}:table/{table_name}/index/GSI1\"\n      ]\n    }\n  ]\n}\n```\n\n## Advanced usage\n\n### IaC Templates\n\nBelow are some infrastructure-as-code templates for popular providers to help you spin up DynamoDB.\n\n<Accordions>\n<Accordion title=\"CDK\">\n\n```js filename=\"stack.js\"\nnew dynamodb.Table(this, `NextAuthTable`, {\n  tableName: \"next-auth\",\n  partitionKey: { name: \"pk\", type: dynamodb.AttributeType.STRING },\n  sortKey: { name: \"sk\", type: dynamodb.AttributeType.STRING },\n  timeToLiveAttribute: \"expires\",\n}).addGlobalSecondaryIndex({\n  indexName: \"GSI1\",\n  partitionKey: { name: \"GSI1PK\", type: dynamodb.AttributeType.STRING },\n  sortKey: { name: \"GSI1SK\", type: dynamodb.AttributeType.STRING },\n})\n```\n\n</Accordion>\n<Accordion title=\"Cloud Formation\">\n\n```yaml filename=\"cloudformation.yaml\"\nNextAuthTable:\n  Type: \"AWS::DynamoDB::Table\"\n  Properties:\n    TableName: next-auth\n    AttributeDefinitions:\n      - AttributeName: pk\n        AttributeType: S\n      - AttributeName: sk\n        AttributeType: S\n      - AttributeName: GSI1PK\n        AttributeType: S\n      - AttributeName: GSI1SK\n        AttributeType: S\n    KeySchema:\n      - AttributeName: pk\n        KeyType: HASH\n      - AttributeName: sk\n        KeyType: RANGE\n    GlobalSecondaryIndexes:\n      - IndexName: GSI1\n        Projection:\n          ProjectionType: ALL\n        KeySchema:\n          - AttributeName: GSI1PK\n            KeyType: HASH\n          - AttributeName: GSI1SK\n            KeyType: RANGE\n    TimeToLiveSpecification:\n      AttributeName: expires\n      Enabled: true\n```\n\n</Accordion>\n<Accordion title=\"Terraform\">\n\n```hcl filename=\"dynamodb.tf\"\nresource \"aws_dynamodb_table\" \"authjs\" {\n  name         = \"auth-js\"\n  billing_mode = \"PAY_PER_REQUEST\" # Alternatively, ON_DEMAND, see https://aws.amazon.com/dynamodb/pricing/\n  hash_key     = \"pk\"\n  range_key    = \"sk\"\n\n  attribute {\n    name = \"pk\"\n    type = \"S\"\n  }\n\n  attribute {\n    name = \"sk\"\n    type = \"S\"\n  }\n\n  attribute {\n    name = \"GSI1PK\"\n    type = \"S\"\n  }\n\n  attribute {\n    name = \"GSI1SK\"\n    type = \"S\"\n  }\n\n  global_secondary_index {\n    hash_key        = \"GSI1PK\"\n    name            = \"GSI1\"\n    projection_type = \"ALL\"\n    range_key       = \"GSI1SK\"\n  }\n\n  ttl {\n    attribute_name = \"expires\"\n    enabled        = true\n  }\n}\n```\n\n</Accordion>\n</Accordions>\n\n### Default schema\n\nThe table respects the single table design pattern. This has many advantages:\n\n- Only one table to manage, monitor and provision.\n- Querying relations is faster than with multi-table schemas (for eg. retrieving all sessions for a user).\n- Only one table needs to be replicated if you want to go multi-region.\n\n![DynamoDB Table](https://i.imgur.com/hGZtWDq.png)\n\nBy default, the adapter expects a table with a partition key `pk` and a sort key `sk`, as well as a global secondary index named `GSI1` with `GSI1PK` as partition key and `GSI1SK` as sorting key. To automatically delete sessions and verification requests after they expire using [dynamodb TTL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html) you should [enable the TTL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/time-to-live-ttl-how-to.html) with attribute name `expires`. You can set whatever you want as the table name and the billing method. You can find the full schema in the table structure section below.\n\n### Using a custom schema\n\nYou can configure your custom table schema by passing the `options` key to the adapter constructor:\n\n```js filename=\"./auth.js\"\nconst adapter = DynamoDBAdapter(client, {\n  tableName: \"custom-table-name\",\n  partitionKey: \"custom-pk\",\n  sortKey: \"custom-sk\",\n  indexName: \"custom-index-name\",\n  indexPartitionKey: \"custom-index-pk\",\n  indexSortKey: \"custom-index-sk\",\n})\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/edgedb.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/edgedb.svg\" height=\"64\" width=\"64\" />\n\n# EdgeDB Adapter\n\n## Resources\n\n- [EdgeDB documentation](https://www.edgedb.com/docs/)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install edgedb @auth/edgedb-adapter\nnpm install @edgedb/generate --save-dev\n```\n\nEnsure you have the EdgeDB CLI installed. Follow the instructions below, or read the [EdgeDB quickstart](https://www.edgedb.com/docs/intro/quickstart) to install the EdgeDB CLI and initialize a project\n\n### Environment Variables\n\n```sh\nAUTH_EDGEDB_DSN=\"edgedb://edgedb:p4ssw0rd@10.0.0.1\"\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```js filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { EdgeDBAdapter } from \"@auth/edgedb-adapter\"\nimport { createClient } from \"edgedb\"\n\nconst client = createClient({ dsn: process.env.AUTH_EDGEDB_DSN })\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: EdgeDBAdapter(client),\n  providers: [],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { EdgeDBAdapter } from \"@auth/edgedb-adapter\"\nimport { createClient } from \"edgedb\"\n\nconst client = createClient({ dsn: import.meta.env.AUTH_EDGEDB_DSN })\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: EdgeDBAdapter(client),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```js filename=\"./auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { EdgeDBAdapter } from \"@auth/edgedb-adapter\"\nimport { createClient } from \"edgedb\"\n\nconst client = createClient({ dsn: process.env.AUTH_EDGEDB_DSN })\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: EdgeDBAdapter(client),\n  providers: [],\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { EdgeDBAdapter } from \"@auth/edgedb-adapter\"\nimport { createClient } from \"edgedb\"\n\nconst app = express()\n\nconst client = createClient({ dsn: process.env.AUTH_EDGEDB_DSN })\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: EdgeDBAdapter(client),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### EdgeDB CLI\n\n#### Linux or macOS\n\n```bash\ncurl --proto '=https' --tlsv1.2 -sSf https://sh.edgedb.com | sh\n```\n\n#### Windows\n\n```powershell\niwr https://ps1.edgedb.com -useb | iex\n```\n\nCheck that the CLI is available with the `edgedb --version` command. If you get a `Command not found` error, you may need to open a new terminal window before the `edgedb` command is available.\n\nOnce the CLI is installed, initialize a project from the application’s root directory. You’ll be presented with a series of prompts.\n\n```bash\nedgedb project init\n```\n\nThis process will spin up an EdgeDB instance and [“link”](https://www.edgedb.com/docs/cli/edgedb_instance/edgedb_instance_link#edgedb-instance-link) it with your current directory. As long as you’re inside that directory, CLI commands and client libraries will be able to connect to the linked instance automatically, without additional configuration.\n\n### Schema\n\nReplace the contents of the auto-generated file in `dbschema/default.esdl` with the following:\n\n```js filename=\"default.esdl\"\nmodule default {\n    type User {\n        property name -> str;\n        required property email -> str {\n            constraint exclusive;\n        }\n        property emailVerified -> datetime;\n        property image -> str;\n        multi link accounts := .<user[is Account];\n        multi link sessions := .<user[is Session];\n        property createdAt -> datetime {\n            default := datetime_current();\n        };\n    }\n\n    type Account {\n       required property userId := .user.id;\n       required property type -> str;\n       required property provider -> str;\n       required property providerAccountId -> str {\n        constraint exclusive;\n       };\n       property refresh_token -> str;\n       property access_token -> str;\n       property expires_at -> int64;\n       property token_type -> str;\n       property scope -> str;\n       property id_token -> str;\n       property session_state -> str;\n       required link user -> User {\n            on target delete delete source;\n       };\n       property createdAt -> datetime {\n            default := datetime_current();\n        };\n\n       constraint exclusive on ((.provider, .providerAccountId))\n    }\n\n    type Session {\n        required property sessionToken -> str {\n            constraint exclusive;\n        }\n        required property userId := .user.id;\n        required property expires -> datetime;\n        required link user -> User {\n            on target delete delete source;\n        };\n        property createdAt -> datetime {\n            default := datetime_current();\n        };\n    }\n\n    type VerificationToken {\n        required property identifier -> str;\n        required property token -> str {\n            constraint exclusive;\n        }\n        required property expires -> datetime;\n        property createdAt -> datetime {\n            default := datetime_current();\n        };\n\n        constraint exclusive on ((.identifier, .token))\n    }\n}\n\n# Disable the application of access policies within access policies\n# themselves. This behavior will become the default in EdgeDB 3.0.\n# See: https://www.edgedb.com/docs/reference/ddl/access_policies#nonrecursive\nusing future nonrecursive_access_policies;\n```\n\n### Migration\n\n1. Create a migration\n\n```\nedgedb migration create\n```\n\n2. Apply the migration\n\n```\nedgedb migrate\n```\n\nTo learn more about [EdgeDB migrations](https://www.edgedb.com/docs/intro/migrations#generate-a-migration) check out the [Migrations docs](https://www.edgedb.com/docs/intro/migrations).\n\n### Generate\n\n```npm2yarn\nnpx @edgedb/generate edgeql-js\n```\n\nThis will generate the [query builder](https://www.edgedb.com/docs/clients/js/querybuilder) so that you can write fully typed EdgeQL queries with TypeScript in a code-first way.\n\n```ts\nconst query = e.select(e.User, () => ({\n  id: true,\n  email: true,\n  emailVerified: true,\n  name: true,\n  image: true,\n  filter_single: { email: \"johndoe@example.com\" },\n}))\n\nreturn await query.run(client)\n```\n\n## Deploying\n\n### Deploy EdgeDB\n\nFirst deploy an EdgeDB instance on your preferred cloud provider:\n\n- [AWS](https://www.edgedb.com/docs/guides/deployment/aws_aurora_ecs)\n- [Google Cloud](https://www.edgedb.com/docs/guides/deployment/gcp)\n- [Azure](https://www.edgedb.com/docs/guides/deployment/azure_flexibleserver)\n- [DigitalOcean](https://www.edgedb.com/docs/guides/deployment/digitalocean)\n- [Fly.io](https://www.edgedb.com/docs/guides/deployment/fly_io)\n- [Docker](https://www.edgedb.com/docs/guides/deployment/docker) (cloud-agnostic)\n\n### Find your instance’s DSN\n\nThe DSN is also known as a connection string. It will have the format `edgedb://username:password@hostname:port`. The exact instructions for this depend on which cloud provider you're deploying to.\n\n### Set an environment variable\n\n```env filename=\".env\"\nAUTH_EDGEDB_DSN=edgedb://johndoe:supersecure@myhost.com:420\n```\n\n### Apply migrations\n\nUse the DSN to apply migrations against your remote instance.\n\n```bash\nedgedb migrate --dsn <your-instance-dsn>\n```\n\n### Set up a `prebuild` script\n\nAdd the following `prebuild` script to your `package.json`. When your hosting provider initializes the build, it will trigger this script which will generate the query builder. The `npx @edgedb/generate edgeql-js` command will read the value of the `EDGEDB_DSN` environment variable, connect to the database, and generate the query builder before your hosting provider starts building the project.\n\n```diff filename=\"package.json\"\n\"scripts\": {\n  \"dev\": \"next dev\",\n  \"build\": \"next build\",\n  \"start\": \"next start\",\n  \"lint\": \"next lint\",\n+  \"prebuild\": \"npx @edgedb/generate edgeql-js\"\n},\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/fauna.mdx",
    "content": "import { Accordion, Accordions } from \"@/components/Accordion\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/fauna.svg\" height=\"64\" width=\"64\" />\n\n# Fauna Adapter\n\n## Resources\n\n- [Fauna documentation](https://docs.fauna.com/fauna/current/)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/fauna-adapter fauna\n```\n\n### Environment Variables\n\n```sh\nAUTH_FAUNA_CLIENT=http://localhost:8443\nAUTH_FAUNA_SECRET=abc123\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { Client } from \"fauna\"\nimport { FaunaAdapter } from \"@auth/fauna-adapter\"\n\nconst client = new Client({\n  secret: process.env.AUTH_FAUNA_SECRET,\n  endpoint: new URL(process.env.AUTH_FAUNA_CLIENT)\n})\n\nexport { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [],\n  adapter: FaunaAdapter(client)\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { Client } from \"fauna\"\nimport { FaunaAdapter } from \"@auth/fauna-adapter\"\n\nconst client = new Client({\n  secret: import.meta.env.AUTH_FAUNA_SECRET,\n  endpoint: new URL(import.meta.env.AUTH_FAUNA_CLIENT),\n})\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: FaunaAdapter(client),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { Client } from \"fauna\"\nimport { FaunaAdapter } from \"@auth/fauna-adapter\"\n\nconst client = new Client({\n  secret: process.env.AUTH_FAUNA_SECRET,\n  endpoint: new URL(process.env.AUTH_FAUNA_CLIENT)\n})\n\nexport { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [],\n  adapter: FaunaAdapter(client)\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { Client } from \"fauna\"\nimport { FaunaAdapter } from \"@auth/fauna-adapter\"\n\nconst app = express()\n\nconst client = new Client({\n  secret: process.env.AUTH_FAUNA_SECRET,\n  endpoint: new URL(process.env.AUTH_FAUNA_CLIENT),\n})\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: FaunaAdapter(client),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Migrating to v2\n\nIn `@auth/adapter-fauna@2.0.0`, we've renamed the collections to use an uppercase naming pattern in accordance with the Fauna best practices. If you're migrating from v1, you'll need to rename your collections to match the new naming scheme. Additionally, we've renamed the indexes to match the new method-like index names (i.e. `account_by_user_id` to `Account.byUserId`). For more information on migrating your Fauna schema, see their migration guide [here](https://docs.fauna.com/fauna/current/migration)\n\n<Accordions>\n  <Accordion title=\"Migration Example\">\n\n```ts filename=\"authjs-adapter-fauna-v2-migration.fql\"\nCollection.byName(\"accounts\")!.update({\n  name: \"Account\"\n  indexes: {\n    byUserId: {\n        terms: [{ field: \"userId\" }]\n    },\n    byProviderAndProviderAccountId: {\n        terms: [{ field: \"provider\" }, { field: \"providerAccountId\" }]\n    },\n    account_by_provider_and_provider_account_id: null,\n    accounts_by_user_id: null\n  }\n})\nCollection.byName(\"sessions\")!.update({\n  name: \"Session\",\n  indexes: {\n    bySessionToken: {\n        terms: [{ field: \"sessionToken\" }]\n    },\n    byUserId: {\n        terms: [{ field: \"userId\" }]\n    },\n    session_by_session_token: null,\n    sessions_by_user_id: null\n  }\n})\nCollection.byName(\"users\")!.update({\n  name: \"User\",\n  indexes: {\n    byEmail: {\n        terms: [{ field: \"email\" }]\n    },\n    user_by_email: null\n  }\n})\nCollection.byName(\"verification_tokens\")!.update({\n  name: \"VerificationToken\",\n  indexes: {\n    byIdentifierAndToken: {\n        terms: [{ field: \"identifier\" }, { field: \"token\" }]\n    },\n    verification_token_by_identifier_and_token: null\n  }\n})\n```\n\n  </Accordion>\n</Accordions>\n\n#### Schema\n\nRun the following commands inside of the `Shell` tab in the Fauna dashboard to setup the appropriate collections and indexes.\n\n```ts filename=\"authjs-fauna-adapter-schema.fql\"\nCollection.create({\n  name: \"Account\",\n  indexes: {\n    byUserId: {\n      terms: [\n        { field: \"userId\" }\n      ]\n    },\n    byProviderAndProviderAccountId: {\n      terms [\n        { field: \"provider\" },\n        { field: \"providerAccountId\" }\n      ]\n    },\n  }\n})\nCollection.create({\n  name: \"Session\",\n  constraints: [\n    {\n      unique: [\"sessionToken\"],\n      status: \"active\",\n    }\n  ],\n  indexes: {\n    bySessionToken: {\n      terms: [\n        { field: \"sessionToken\" }\n      ]\n    },\n    byUserId: {\n      terms [\n        { field: \"userId\" }\n      ]\n    },\n  }\n})\nCollection.create({\n  name: \"User\",\n  constraints: [\n    {\n      unique: [\"email\"],\n      status: \"active\",\n    }\n  ],\n  indexes: {\n    byEmail: {\n      terms [\n        { field: \"email\" }\n      ]\n    },\n  }\n})\nCollection.create({\n  name: \"VerificationToken\",\n  indexes: {\n    byIdentifierAndToken: {\n      terms [\n        { field: \"identifier\" },\n        { field: \"token\" }\n      ]\n    },\n  }\n})\n```\n\n#### Custom Collection Names\n\nIf you want to use custom collection names, you can pass them as an option to the adapter, like this:\n\n```js\nFaunaAdapter(client, {\n  collectionNames: {\n    user: \"CustomUser\",\n    account: \"CustomAccount\",\n    session: \"CustomSession\",\n    verificationToken: \"CustomVerificationToken\",\n  },\n})\n```\n\nMake sure the collection names you pass to the provider match the collection names of your Fauna database.\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/firebase.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/firebase.svg\" height=\"64\" width=\"64\" />\n\n# Firebase Adapter\n\n> Using the [Firebase Admin SDK](https://firebase.google.com/docs/admin/setup) and [Firestore](https://firebase.google.com/docs/firestore).\n\n## Resources\n\n- [Firebase Admin documentation](https://firebase.google.com/docs/admin/setup)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/firebase-adapter firebase-admin\n```\n\n### Environment variables\n\n```sh\n// Auth via Service Account File\nGOOGLE_APPLICATION_CREDENTIALS\n\n// Auth via key values\nAUTH_FIREBASE_PROJECT_ID\nAUTH_FIREBASE_CLIENT_EMAIL\nAUTH_FIREBASE_PRIVATE_KEY\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { FirestoreAdapter } from \"@auth/firebase-adapter\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [],\n  adapter: FirestoreAdapter(),\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { FirestoreAdapter } from \"@auth/firebase-adapter\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: FirestoreAdapter(),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { FirestoreAdapter } from \"@auth/firebase-adapter\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [],\n  adapter: FirestoreAdapter(),\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { FirestoreAdapter } from \"@auth/firebase-adapter\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: FirestoreAdapter(),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Authentication\n\n#### Service Account File\n\nFirst, create a Firebase project and generate a service account key. Visit: `https://console.firebase.google.com/u/0/project/{project-id}/settings/serviceaccounts/adminsdk` (replace `{project-id}` with your project's id)\n\n1. Download the service account key and save it in your project. (Make sure to add the file to your `.gitignore`!)\n2. Add [`GOOGLE_APPLICATION_CREDENTIALS`](https://cloud.google.com/docs/authentication/application-default-credentials#GAC) to your environment variables and point it to the service account key file.\n3. The adapter will automatically pick up the environment variable and use it to authenticate with the Firebase Admin SDK. You do not need to pass any additional authentication options to the adapter.\n\n### Service Account Values\n\n1. Download the service account key to a temporary location (Don't commit this file!).\n2. Add the following environment variables to your project  \n   a. `AUTH_FIREBASE_PROJECT_ID`  \n   b. `AUTH_FIREBASE_CLIENT_EMAIL`  \n   c. `AUTH_FIREBASE_PRIVATE_KEY`\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { FirestoreAdapter } from \"@auth/firebase-adapter\"\nimport { cert } from \"firebase-admin/app\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: FirestoreAdapter({\n    credential: cert({\n      projectId: process.env.AUTH_FIREBASE_PROJECT_ID,\n      clientEmail: process.env.AUTH_FIREBASE_CLIENT_EMAIL,\n      privateKey: process.env.AUTH_FIREBASE_PRIVATE_KEY,\n    }),\n  }),\n})\n```\n\n### Using an existing Firestore instance\n\nIf you already have a Firestore instance, you can pass that to the adapter directly instead.\n\n<Callout>\n  When passing an instance and in a serverless environment, remember to handle\n  duplicate app initialization.\n</Callout>\n\n<Callout type=\"info\">\n  You can use the `initFirestore` utility to initialize the app and get an\n  instance safely.\n</Callout>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { FirestoreAdapter } from \"@auth/firebase-adapter\"\nimport { firestore } from \"lib/firestore\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: FirestoreAdapter(firestore),\n})\n```\n\nUtility function that helps making sure that there is no duplicate app initialization issues in serverless environments.\nIf no parameter is passed, it will use the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to initialize a Firestore instance.\n\n```ts filename=\"lib/firestore.ts\"\nimport { initFirestore } from \"@auth/firebase-adapter\"\nimport { cert } from \"firebase-admin/app\"\n\nexport const firestore = initFirestore({\n  credential: cert({\n    projectId: process.env.FIREBASE_PROJECT_ID,\n    clientEmail: process.env.FIREBASE_CLIENT_EMAIL,\n    privateKey: process.env.FIREBASE_PRIVATE_KEY,\n  }),\n})\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/hasura.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/hasura.svg\" width=\"64\" height=\"64\" />\n\n# Hasura Adapter\n\n## Resources\n\n- [Hasura documentation](https://hasura.io/docs)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/hasura-adapter\n```\n\n### Environment variables\n\n```sh\nAUTH_HASURA_GRAPHQL=http://localhost:8000/graphql\nAUTH_HASURA_SECRET=abc123\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { HasuraAdapter } from \"@auth/hasura-adapter\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: HasuraAdapter({\n    endpoint: process.env.AUTH_HASURA_GRAPHQL,\n    adminSecret: process.env.AUTH_HASURA_SECRET,\n  }),\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { HasuraAdapter } from \"@auth/hasura-adapter\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: HasuraAdapter({\n      endpoint: import.meta.env.AUTH_HASURA_GRAPHQL,\n      adminSecret: import.meta.env.AUTH_HASURA_SECRET,\n    }),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { HasuraAdapter } from \"@auth/hasura-adapter\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: HasuraAdapter({\n    endpoint: process.env.AUTH_HASURA_GRAPHQL,\n    adminSecret: process.env.AUTH_HASURA_SECRET,\n  }),\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { HasuraAdapter } from \"@auth/hasura-adapter\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: HasuraAdapter({\n      endpoint: process.env.AUTH_HASURA_GRAPHQL,\n      adminSecret: process.env.AUTH_HASURA_SECRET,\n    }),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Migrations\n\n1. Create the Auth.js schema in your database using SQL.\n\n```sql\nCREATE TABLE accounts (\n    id uuid DEFAULT gen_random_uuid() NOT NULL,\n    type text NOT NULL,\n    provider text NOT NULL,\n    \"providerAccountId\" text NOT NULL,\n    refresh_token text,\n    access_token text,\n    expires_at integer,\n    token_type text,\n    scope text,\n    id_token text,\n    session_state text,\n    \"userId\" uuid NOT NULL\n);\n\nCREATE TABLE sessions (\n    id uuid DEFAULT gen_random_uuid() NOT NULL,\n    \"sessionToken\" text NOT NULL,\n    \"userId\" uuid NOT NULL,\n    expires timestamptz NOT NULL\n);\n\nCREATE TABLE users (\n    id uuid DEFAULT gen_random_uuid() NOT NULL,\n    name text,\n    email text NOT NULL,\n    \"emailVerified\" timestamptz,\n    image text\n);\n\nCREATE TABLE verification_tokens (\n    token text NOT NULL,\n    identifier text NOT NULL,\n    expires timestamptz NOT NULL\n);\n\nCREATE TABLE provider_type (\n    value text NOT NULL\n);\n\nALTER TABLE ONLY accounts\n    ADD CONSTRAINT accounts_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY sessions\n    ADD CONSTRAINT sessions_pkey PRIMARY KEY (\"sessionToken\");\n\nALTER TABLE ONLY users\n    ADD CONSTRAINT users_email_key UNIQUE (email);\n\nALTER TABLE ONLY users\n    ADD CONSTRAINT users_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY verification_tokens\n    ADD CONSTRAINT verification_tokens_pkey PRIMARY KEY (token);\n\nALTER TABLE ONLY provider_type\n    ADD CONSTRAINT provider_type_pkey PRIMARY KEY (value);\n\nALTER TABLE ONLY accounts\n    ADD CONSTRAINT \"accounts_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES public.users(id) ON UPDATE RESTRICT ON DELETE CASCADE;\n\nALTER TABLE ONLY sessions\n    ADD CONSTRAINT \"sessions_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES public.users(id) ON UPDATE RESTRICT ON DELETE CASCADE;\n\nINSERT INTO provider_type (value) VALUES ('credentials'), ('email'), ('oauth'), ('oidc');\n\nALTER TABLE ONLY accounts\n    ADD CONSTRAINT \"accounts_type_fkey\" FOREIGN KEY (\"type\") REFERENCES public.provider_type(value) ON UPDATE RESTRICT ON DELETE RESTRICT;\n```\n\n<Callout>\n  Tips: [Track all the tables and relationships in\n  Hasura](https://hasura.io/docs/latest/schema/postgres/using-existing-database/#step-1-track-tablesviews)\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/kysely.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/kysely.svg\" width=\"64\" height=\"64\" />\n\n# Kysely Adapter\n\n## Resources\n\n- [Kysely documentation](https://kysely.dev/docs/intro)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install kysely @auth/kysely-adapter\n```\n\n### Environment Variables\n\n```sh\nDATABASE_HOST=\nDATABASE_NAME=\nDATABASE_USER=\nDATABASE_PASSWORD=\n```\n\n### Configuration\n\nThis adapter supports the same first party dialects that Kysely (as of v0.24.2) supports: PostgreSQL, MySQL, and SQLite. The examples below use PostgreSQL with the [pg](https://www.npmjs.com/package/pg) client.\n\n```bash npm2yarn\nnpm install pg\nnpm install --save-dev @types/pg\n```\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { KyselyAdapter } from \"@auth/kysely-adapter\"\nimport { db } from \"../../../db\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: KyselyAdapter(db),\n  providers: [],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { KyselyAdapter } from \"@auth/kysely-adapter\"\nimport { db } from \"../../../db\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: KyselyAdapter(db),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { KyselyAdapter } from \"@auth/kysely-adapter\"\nimport { db } from \"../../../db\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: KyselyAdapter(db),\n  providers: [],\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { KyselyAdapter } from \"@auth/kysely-adapter\"\nimport { db } from \"../../../db\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: KyselyAdapter(db),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\nKysely's constructor requires a database interface that contains an entry with an interface for each of your tables. You can define these types manually, or use `kysely-codegen` / `prisma-kysely` to automatically generate them. Check out the default [models](/guides/creating-a-database-adapter) required by Auth.js.\n\n```ts filename=\"db.ts\"\nimport { PostgresDialect } from \"kysely\"\nimport { Pool } from \"pg\"\n\n// This adapter exports a wrapper of the original `Kysely` class called `KyselyAuth`,\n// that can be used to provide additional type-safety.\n// While using it isn't required, it is recommended as it will verify\n// that the database interface has all the fields that Auth.js expects.\nimport { KyselyAuth } from \"@auth/kysely-adapter\"\n\nimport type { GeneratedAlways } from \"kysely\"\n\ninterface Database {\n  User: {\n    id: GeneratedAlways<string>\n    name: string | null\n    email: string\n    emailVerified: Date | null\n    image: string | null\n  }\n  Account: {\n    id: GeneratedAlways<string>\n    userId: string\n    type: string\n    provider: string\n    providerAccountId: string\n    refresh_token: string | null\n    access_token: string | null\n    expires_at: number | null\n    token_type: string | null\n    scope: string | null\n    id_token: string | null\n    session_state: string | null\n  }\n  Session: {\n    id: GeneratedAlways<string>\n    userId: string\n    sessionToken: string\n    expires: Date\n  }\n  VerificationToken: {\n    identifier: string\n    token: string\n    expires: Date\n  }\n}\n\nexport const db = new KyselyAuth<Database>({\n  dialect: new PostgresDialect({\n    pool: new Pool({\n      host: process.env.DATABASE_HOST,\n      database: process.env.DATABASE_NAME,\n      user: process.env.DATABASE_USER,\n      password: process.env.DATABASE_PASSWORD,\n    }),\n  }),\n})\n```\n\n<Callout>\nAn alternative to manually defining types is generating them from the database schema using [kysely-codegen](https://github.com/RobinBlomberg/kysely-codegen), or from Prisma schemas using [prisma-kysely](https://github.com/valtyr/prisma-kysely). When using generated types with `KyselyAuth`, import `Codegen` and pass it as the second generic arg:\n\n```ts\nimport type { Codegen } from \"@auth/kysely-adapter\"\nnew KyselyAuth<Database, Codegen>()\n```\n\n</Callout>\n\n### Schema\n\n```ts filename=\"db/migrations/001_create_db.ts\"\nimport { Kysely, sql } from \"kysely\"\n\nexport async function up(db: Kysely<any>): Promise<void> {\n  await db.schema\n    .createTable(\"User\")\n    .addColumn(\"id\", \"uuid\", (col) =>\n      col.primaryKey().defaultTo(sql`gen_random_uuid()`)\n    )\n    .addColumn(\"name\", \"text\")\n    .addColumn(\"email\", \"text\", (col) => col.unique().notNull())\n    .addColumn(\"emailVerified\", \"timestamptz\")\n    .addColumn(\"image\", \"text\")\n    .execute()\n\n  await db.schema\n    .createTable(\"Account\")\n    .addColumn(\"id\", \"uuid\", (col) =>\n      col.primaryKey().defaultTo(sql`gen_random_uuid()`)\n    )\n    .addColumn(\"userId\", \"uuid\", (col) =>\n      col.references(\"User.id\").onDelete(\"cascade\").notNull()\n    )\n    .addColumn(\"type\", \"text\", (col) => col.notNull())\n    .addColumn(\"provider\", \"text\", (col) => col.notNull())\n    .addColumn(\"providerAccountId\", \"text\", (col) => col.notNull())\n    .addColumn(\"refresh_token\", \"text\")\n    .addColumn(\"access_token\", \"text\")\n    .addColumn(\"expires_at\", \"bigint\")\n    .addColumn(\"token_type\", \"text\")\n    .addColumn(\"scope\", \"text\")\n    .addColumn(\"id_token\", \"text\")\n    .addColumn(\"session_state\", \"text\")\n    .execute()\n\n  await db.schema\n    .createTable(\"Session\")\n    .addColumn(\"id\", \"uuid\", (col) =>\n      col.primaryKey().defaultTo(sql`gen_random_uuid()`)\n    )\n    .addColumn(\"userId\", \"uuid\", (col) =>\n      col.references(\"User.id\").onDelete(\"cascade\").notNull()\n    )\n    .addColumn(\"sessionToken\", \"text\", (col) => col.notNull().unique())\n    .addColumn(\"expires\", \"timestamptz\", (col) => col.notNull())\n    .execute()\n\n  await db.schema\n    .createTable(\"VerificationToken\")\n    .addColumn(\"identifier\", \"text\", (col) => col.notNull())\n    .addColumn(\"token\", \"text\", (col) => col.notNull().unique())\n    .addColumn(\"expires\", \"timestamptz\", (col) => col.notNull())\n    .execute()\n\n  await db.schema\n    .createIndex(\"Account_userId_index\")\n    .on(\"Account\")\n    .column(\"userId\")\n    .execute()\n\n  await db.schema\n    .createIndex(\"Session_userId_index\")\n    .on(\"Session\")\n    .column(\"userId\")\n    .execute()\n}\n\nexport async function down(db: Kysely<any>): Promise<void> {\n  await db.schema.dropTable(\"Account\").ifExists().execute()\n  await db.schema.dropTable(\"Session\").ifExists().execute()\n  await db.schema.dropTable(\"User\").ifExists().execute()\n  await db.schema.dropTable(\"VerificationToken\").ifExists().execute()\n}\n```\n\n> This schema is adapted for use in Kysely and is based upon our main [schema](/reference/core/adapters#models).\n\nFor more information about creating and running migrations with Kysely, refer to the [Kysely migrations documentation](https://kysely.dev/docs/migrations).\n\n### Naming conventions\n\nIf mixed snake_case and camelCase column names is an issue for you and/or your underlying database system, we recommend using Kysely's `CamelCasePlugin` ([see the documentation here](https://kysely-org.github.io/kysely-apidoc/classes/CamelCasePlugin.html)) feature to change the field names. This won't affect NextAuth.js, but will allow you to have consistent casing when using Kysely.\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/mikro-orm.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/mikro-orm.svg\" width=\"64\" height=\"64\" />\n\n# MikroORM Adapter\n\n## Resources\n\n- [MikroORM documentation](https://mikro-orm.io/docs/installation)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @mikro-orm/core @auth/mikro-orm-adapter\n```\n\n### Environment Variables\n\n```sh\nDATABASE_CONNECTION_STRING=./db.sqlite\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { MikroOrmAdapter } from \"@auth/mikro-orm-adapter\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: MikroOrmAdapter({\n    // MikroORM options object - https://mikro-orm.io/docs/next/configuration#driver\n    dbName: process.env.DATABASE_CONNECTION_STRING,\n    type: \"sqlite\",\n    debug: true,\n  }),\n  providers: [],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { MikroOrmAdapter } from \"@auth/mikro-orm-adapter\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: MikroOrmAdapter({\n      // MikroORM options object - https://mikro-orm.io/docs/next/configuration#driver\n      dbName: import.meta.env.DATABASE_CONNECTION_STRING,\n      type: \"sqlite\",\n      debug: true,\n    }),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { MikroOrmAdapter } from \"@auth/mikro-orm-adapter\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: MikroOrmAdapter({\n    // MikroORM options object - https://mikro-orm.io/docs/next/configuration#driver\n    dbName: process.env.DATABASE_CONNECTION_STRING,\n    type: \"sqlite\",\n    debug: true,\n  }),\n  providers: [],\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { MikroOrmAdapter } from \"@auth/mikro-orm-adapter\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: MikroOrmAdapter({\n      // MikroORM options object - https://mikro-orm.io/docs/next/configuration#driver\n      dbName: process.env.DATABASE_CONNECTION_STRING,\n      type: \"sqlite\",\n      debug: true,\n    }),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Advanced usage\n\n#### Passing custom entities\n\nThe MikroORM adapter ships with its own set of entities. If you'd like to extend them, you can optionally pass them to the adapter.\n\n> This schema is adapted for use in MikroORM and based upon our main [schema](https://authjs.dev/reference/core/adapters#models)\n\n```ts filename=\"./auth.ts\"\nimport config from \"config/mikro-orm.ts\"\nimport {\n  Cascade,\n  Collection,\n  Entity,\n  OneToMany,\n  PrimaryKey,\n  Property,\n  Unique,\n} from \"@mikro-orm/core\"\nimport { defaultEntities } from \"@auth/mikro-orm-adapter\"\n\nconst type { Account, Session } = defaultEntities\n\n@Entity()\nexport class User implements defaultEntities.User {\n  @PrimaryKey()\n  id: string = randomUUID()\n\n  @Property({ nullable: true })\n  name?: string\n\n  @Property({ nullable: true })\n  @Unique()\n  email?: string\n\n  @Property({ type: \"Date\", nullable: true })\n  emailVerified: Date | null = null\n\n  @Property({ nullable: true })\n  image?: string\n\n  @OneToMany({\n    entity: () => Session,\n    mappedBy: (session) => session.user,\n    hidden: true,\n    orphanRemoval: true,\n    cascade: [Cascade.ALL],\n  })\n  sessions = new Collection<Session>(this)\n\n  @OneToMany({\n    entity: () => Account,\n    mappedBy: (account) => account.user,\n    hidden: true,\n    orphanRemoval: true,\n    cascade: [Cascade.ALL],\n  })\n  accounts = new Collection<Account>(this)\n\n  @Enum({ hidden: true })\n  role = \"ADMIN\"\n}\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: MikroOrmAdapter(config, { entities: { User } }),\n})\n```\n\n#### Including default entities\n\nYou may want to include the defaultEntities in your MikroORM configuration to include them in Migrations etc.\n\nTo achieve that include them in your \"entities\" array:\n\n```typescript filename=\"config/mikro-orm.ts\"\nimport { Options } from \"@mikro-orm/core\"\nimport { defaultEntities } from \"@auth/mikro-orm-adapter\"\n\nconst config: Options = {\n  entities: [VeryImportantEntity, ...Object.values(defaultEntities)],\n}\n\nexport default config\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/mongodb.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/mongodb.svg\" width=\"48\" height=\"48\" />\n\n# MongoDB Adapter\n\n## Resources\n\n- [MongoDB documentation](https://docs.mongodb.com/)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/mongodb-adapter mongodb\n```\n\n### Environment Variables\n\n```sh\nMONGODB_URI=\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { MongoDBAdapter } from \"@auth/mongodb-adapter\"\nimport client from \"./lib/db\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: MongoDBAdapter(client),\n  providers: [],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { MongoDBAdapter } from \"@auth/mongodb-adapter\"\nimport client from \"./lib/db\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: MongoDBAdapter(client),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { MongoDBAdapter } from \"@auth/mongodb-adapter\"\nimport client from \"./lib/db\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: MongoDBAdapter(client),\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { MongoDBAdapter } from \"@auth/mongodb-adapter\"\nimport client from \"./lib/db\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: MongoDBAdapter(client),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\nThe MongoDB adapter does not handle connections automatically, so you will have to make sure that you pass the Adapter a `MongoClient` that is connected already.\n\n### Add the MongoDB client\n\n```ts filename=\"lib/db.ts\"\n// This approach is taken from https://github.com/vercel/next.js/tree/canary/examples/with-mongodb\nimport { MongoClient, ServerApiVersion } from \"mongodb\"\n\nif (!process.env.MONGODB_URI) {\n  throw new Error('Invalid/Missing environment variable: \"MONGODB_URI\"')\n}\n\nconst uri = process.env.MONGODB_URI\nconst options = {\n  serverApi: {\n    version: ServerApiVersion.v1,\n    strict: true,\n    deprecationErrors: true,\n  },\n}\n\nlet client: MongoClient\n\nif (process.env.NODE_ENV === \"development\") {\n  // In development mode, use a global variable so that the value\n  // is preserved across module reloads caused by HMR (Hot Module Replacement).\n  let globalWithMongo = global as typeof globalThis & {\n    _mongoClient?: MongoClient\n  }\n\n  if (!globalWithMongo._mongoClient) {\n    globalWithMongo._mongoClient = new MongoClient(uri, options)\n  }\n  client = globalWithMongo._mongoClient\n} else {\n  // In production mode, it's best to not use a global variable.\n  client = new MongoClient(uri, options)\n}\n\n// Export a module-scoped MongoClient. By doing this in a\n// separate module, the client can be shared across functions.\nexport default client\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/neo4j.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/neo4j.svg\" height=\"64\" width=\"128\" />\n\n# Neo4j Adapter\n\n## Resources\n\n- [Neo4j documentation](https://neo4j.com/docs/)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/neo4j-adapter neo4j-driver\n```\n\n### Environment Variables\n\n```sh\nNEO4J_URI=bolt://localhost\nNEO4J_USERNAME=neo4j\nNEO4J_PASSWORD=abc\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport neo4j from \"neo4j-driver\"\nimport { Neo4jAdapter } from \"@auth/neo4j-adapter\"\n\nconst driver = neo4j.driver(\n  process.env.NEO4J_URI,\n  neo4j.auth.basic(process.env.NEO4J_USERNAME, process.env.NEO4J_PASSWORD)\n)\n\nconst neo4jSession = driver.session()\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [],\n  adapter: Neo4jAdapter(neo4jSession),\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport neo4j from \"neo4j-driver\"\nimport { Neo4jAdapter } from \"@auth/neo4j-adapter\"\n\nconst driver = neo4j.driver(\n  import.meta.env.NEO4J_URI,\n  neo4j.auth.basic(\n    import.meta.env.NEO4J_USERNAME,\n    import.meta.env.NEO4J_PASSWORD\n  )\n)\n\nconst neo4jSession = driver.session()\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: Neo4jAdapter(neo4jSession),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport neo4j from \"neo4j-driver\"\nimport { Neo4jAdapter } from \"@auth/neo4j-adapter\"\n\nconst driver = neo4j.driver(\n  process.env.NEO4J_URI,\n  neo4j.auth.basic(process.env.NEO4J_USERNAME, process.env.NEO4J_PASSWORD)\n)\n\nconst neo4jSession = driver.session()\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [],\n  adapter: Neo4jAdapter(neo4jSession),\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport neo4j from \"neo4j-driver\"\nimport { Neo4jAdapter } from \"@auth/neo4j-adapter\"\n\nconst app = express()\n\nconst driver = neo4j.driver(\n  process.env.NEO4J_URI,\n  neo4j.auth.basic(process.env.NEO4J_USERNAME, process.env.NEO4J_PASSWORD)\n)\n\nconst neo4jSession = driver.session()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: Neo4jAdapter(neo4jSession),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Schema\n\n#### Node labels\n\nThe following node labels are used.\n\n- User\n- Account\n- Session\n- VerificationToken\n\n#### Relationships\n\nThe following relationships and relationship labels are used.\n\n- `(:User)-[:HAS_ACCOUNT]->(:Account)`\n- `(:User)-[:HAS_SESSION]->(:Session)`\n\n#### Properties\n\nThis schema is adapted for use in Neo4j and is based upon our main [models](https://authjs.dev/reference/core/adapters#models). Please check there for the node properties. Relationships have no properties.\n\n#### Indexes\n\nOptimum indexes will vary on your edition of Neo4j i.e. community or enterprise, and in case you have your own additional data on the nodes. Below are basic suggested indexes.\n\n1. For **both** Community Edition & Enterprise Edition create constraints and indexes\n\n```sql\nCREATE CONSTRAINT user_id_constraint IF NOT EXISTS\nON (u:User) ASSERT u.id IS UNIQUE;\n\nCREATE INDEX user_id_index IF NOT EXISTS\nFOR (u:User) ON (u.id);\n\nCREATE INDEX user_email_index IF NOT EXISTS\nFOR (u:User) ON (u.email);\n\nCREATE CONSTRAINT session_session_token_constraint IF NOT EXISTS\nON (s:Session) ASSERT s.sessionToken IS UNIQUE;\n\nCREATE INDEX session_session_token_index IF NOT EXISTS\nFOR (s:Session) ON (s.sessionToken);\n```\n\n2a. For Community Edition **only** create single-property indexes\n\n```sql\nCREATE INDEX account_provider_index IF NOT EXISTS\nFOR (a:Account) ON (a.provider);\n\nCREATE INDEX account_provider_account_id_index IF NOT EXISTS\nFOR (a:Account) ON (a.providerAccountId);\n\nCREATE INDEX verification_token_identifier_index IF NOT EXISTS\nFOR (v:VerificationToken) ON (v.identifier);\n\nCREATE INDEX verification_token_token_index IF NOT EXISTS\nFOR (v:VerificationToken) ON (v.token);\n```\n\n2b. For Enterprise Edition **only** create composite node key constraints and indexes\n\n```sql\nCREATE CONSTRAINT account_provider_composite_constraint IF NOT EXISTS\nON (a:Account) ASSERT (a.provider, a.providerAccountId) IS NODE KEY;\n\nCREATE INDEX account_provider_composite_index IF NOT EXISTS\nFOR (a:Account) ON (a.provider, a.providerAccountId);\n\nCREATE CONSTRAINT verification_token_composite_constraint IF NOT EXISTS\nON (v:VerificationToken) ASSERT (v.identifier, v.token) IS NODE KEY;\n\nCREATE INDEX verification_token_composite_index IF NOT EXISTS\nFOR (v:VerificationToken) ON (v.identifier, v.token);\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/neon.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/neon.svg\" height=\"64\" width=\"64\" />\n\n# Neon Adapter\n\n## Resources\n\n- [Neon documentation](https://neon.tech/docs/)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/neon-adapter @neondatabase/serverless\n```\n\n### Environment Variables\n\n```sh\nDATABASE_URL=\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport NeonAdapter from \"@auth/neon-adapter\"\nimport { Pool } from \"@neondatabase/serverless\"\n\n// *DO NOT* create a `Pool` here, outside the request handler.\n\nexport const { handlers, auth, signIn, signOut } = NextAuth(() => {\n  // Create a `Pool` inside the request handler.\n  const pool = new Pool({ connectionString: process.env.DATABASE_URL })\n  return {\n    adapter: NeonAdapter(pool),\n    providers: [],\n  }\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport NeonAdapter from \"@auth/neon-adapter\"\nimport { Pool } from \"@neondatabase/serverless\"\n\n// *DO NOT* create a `Pool` here, outside the request handler.\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => {\n    // Create a `Pool` inside the request handler.\n    const pool = new Pool({ connectionString: process.env.DATABASE_URL })\n    return {\n      adapter: NeonAdapter(pool),\n      providers: [],\n    }\n  }\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport NeonAdapter from \"@auth/neon-adapter\"\nimport { Pool } from \"@neondatabase/serverless\"\n\n// *DO NOT* create a `Pool` here, outside the request handler.\n\nexport const { handle, signIn, signOut } = SvelteKitAuth(() => {\n  // Create a `Pool` inside the request handler.\n  const pool = new Pool({ connectionString: process.env.DATABASE_URL })\n  return {\n    adapter: NeonAdapter(pool),\n    providers: [],\n  }\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport NeonAdapter from \"@auth/neon-adapter\"\nimport { Pool } from \"@neondatabase/serverless\"\n\nconst pool = new Pool({ connectionString: process.env.DATABASE_URL })\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: NeonAdapter(pool),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Schema\n\nThe SQL schema for the tables used by this adapter is as follows. Learn more about the models at our\ndoc page on [Database Models](/guides/creating-a-database-adapter).\n\n```sql\nCREATE TABLE verification_token\n(\n  identifier TEXT NOT NULL,\n  expires TIMESTAMPTZ NOT NULL,\n  token TEXT NOT NULL,\n\n  PRIMARY KEY (identifier, token)\n);\n\nCREATE TABLE accounts\n(\n  id SERIAL,\n  \"userId\" INTEGER NOT NULL,\n  type VARCHAR(255) NOT NULL,\n  provider VARCHAR(255) NOT NULL,\n  \"providerAccountId\" VARCHAR(255) NOT NULL,\n  refresh_token TEXT,\n  access_token TEXT,\n  expires_at BIGINT,\n  id_token TEXT,\n  scope TEXT,\n  session_state TEXT,\n  token_type TEXT,\n\n  PRIMARY KEY (id)\n);\n\nCREATE TABLE sessions\n(\n  id SERIAL,\n  \"userId\" INTEGER NOT NULL,\n  expires TIMESTAMPTZ NOT NULL,\n  \"sessionToken\" VARCHAR(255) NOT NULL,\n\n  PRIMARY KEY (id)\n);\n\nCREATE TABLE users\n(\n  id SERIAL,\n  name VARCHAR(255),\n  email VARCHAR(255),\n  \"emailVerified\" TIMESTAMPTZ,\n  image TEXT,\n\n  PRIMARY KEY (id)\n);\n\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/pg.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/pg.svg\" height=\"64\" width=\"64\" />\n\n# PostgreSQL Adapter\n\n## Resources\n\n- [Pg documentation](https://www.postgresql.org/docs/)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/pg-adapter pg\n```\n\n### Environment Variables\n\n```sh\nDATABASE_HOST=\nDATABASE_NAME=\nDATABASE_USER=\nDATABASE_PASSWORD=\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport PostgresAdapter from \"@auth/pg-adapter\"\nimport { Pool } from \"pg\"\n\nconst pool = new Pool({\n  host: process.env.DATABASE_HOST,\n  user: process.env.DATABASE_USER,\n  password: process.env.DATABASE_PASSWORD,\n  database: process.env.DATABASE_NAME,\n  max: 20,\n  idleTimeoutMillis: 30000,\n  connectionTimeoutMillis: 2000,\n})\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: PostgresAdapter(pool),\n  providers: [],\n})\n```\n\nIf you are using [Neon](https://neon.tech)'s PostgreSQL like [Vercel Postgres](https://vercel.com/docs/storage/vercel-postgres), you can use `@neondatabase/serverless` to work with edge runtime.\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport PostgresAdapter from \"@auth/pg-adapter\"\nimport { Pool } from \"@neondatabase/serverless\"\n\n// *DO NOT* create a `Pool` here, outside the request handler.\n// Neon's Postgres cannot keep a pool alive between requests.\n\nexport const { handlers, auth, signIn, signOut } = NextAuth(() => {\n  // Create a `Pool` inside the request handler.\n  const pool = new Pool({ connectionString: process.env.DATABASE_URL })\n  return {\n    adapter: PostgresAdapter(pool),\n    providers: [],\n  }\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport PostgresAdapter from \"@auth/pg-adapter\"\nimport { Pool } from \"pg\"\n\nconst pool = new Pool({\n  host: import.meta.env.DATABASE_HOST,\n  user: import.meta.env.DATABASE_USER,\n  password: import.meta.env.DATABASE_PASSWORD,\n  database: import.meta.env.DATABASE_NAME,\n  max: 20,\n  idleTimeoutMillis: 30000,\n  connectionTimeoutMillis: 2000,\n})\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: PostgresAdapter(pool),\n  })\n)\n```\n\nIf you are using [Neon](https://neon.tech)'s PostgreSQL like [Vercel Postgres](https://vercel.com/docs/storage/vercel-postgres), you can use `@neondatabase/serverless` to work with edge runtime.\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport PostgresAdapter from \"@auth/pg-adapter\"\nimport { Pool } from \"@neondatabase/serverless\"\n\n// *DO NOT* create a `Pool` here, outside the request handler.\n// Neon's Postgres cannot keep a pool alive between requests.\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => {\n    // Create a `Pool` inside the request handler.\n    const pool = new Pool({ connectionString: import.meta.env.DATABASE_URL })\n    return {\n      providers: [],\n      adapter: PostgresAdapter(pool),\n    }\n  }\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport PostgresAdapter from \"@auth/pg-adapter\"\nimport { Pool } from \"pg\"\n\nconst pool = new Pool({\n  host: process.env.DATABASE_HOST,\n  user: process.env.DATABASE_USER,\n  password: process.env.DATABASE_PASSWORD,\n  database: process.env.DATABASE_NAME,\n  max: 20,\n  idleTimeoutMillis: 30000,\n  connectionTimeoutMillis: 2000,\n})\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: PostgresAdapter(pool),\n  providers: [],\n})\n```\n\nIf you are using [Neon](https://neon.tech)'s PostgreSQL like [Vercel Postgres](https://vercel.com/docs/storage/vercel-postgres), you can use `@neondatabase/serverless` to work with edge runtime.\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport PostgresAdapter from \"@auth/pg-adapter\"\nimport { Pool } from \"@neondatabase/serverless\"\n\n// *DO NOT* create a `Pool` here, outside the request handler.\n// Neon's Postgres cannot keep a pool alive between requests.\n\nexport const { handle, signIn, signOut } = SvelteKitAuth(() => {\n  // Create a `Pool` inside the request handler.\n  const pool = new Pool({ connectionString: process.env.DATABASE_URL })\n  return {\n    adapter: PostgresAdapter(pool),\n    providers: [],\n  }\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport PostgresAdapter from \"@auth/pg-adapter\"\nimport { Pool } from \"pg\"\n\nconst pool = new Pool({\n  host: process.env.DATABASE_HOST,\n  user: process.env.DATABASE_USER,\n  password: process.env.DATABASE_PASSWORD,\n  database: process.env.DATABASE_NAME,\n  max: 20,\n  idleTimeoutMillis: 30000,\n  connectionTimeoutMillis: 2000,\n})\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: PostgresAdapter(pool),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Schema\n\nThe SQL schema for the tables used by this adapter is as follows. Learn more about the models at our\ndoc page on [Database Models](/guides/creating-a-database-adapter).\n\n```sql\nCREATE TABLE verification_token\n(\n  identifier TEXT NOT NULL,\n  expires TIMESTAMPTZ NOT NULL,\n  token TEXT NOT NULL,\n\n  PRIMARY KEY (identifier, token)\n);\n\nCREATE TABLE accounts\n(\n  id SERIAL,\n  \"userId\" INTEGER NOT NULL,\n  type VARCHAR(255) NOT NULL,\n  provider VARCHAR(255) NOT NULL,\n  \"providerAccountId\" VARCHAR(255) NOT NULL,\n  refresh_token TEXT,\n  access_token TEXT,\n  expires_at BIGINT,\n  id_token TEXT,\n  scope TEXT,\n  session_state TEXT,\n  token_type TEXT,\n\n  PRIMARY KEY (id)\n);\n\nCREATE TABLE sessions\n(\n  id SERIAL,\n  \"userId\" INTEGER NOT NULL,\n  expires TIMESTAMPTZ NOT NULL,\n  \"sessionToken\" VARCHAR(255) NOT NULL,\n\n  PRIMARY KEY (id)\n);\n\nCREATE TABLE users\n(\n  id SERIAL,\n  name VARCHAR(255),\n  email VARCHAR(255),\n  \"emailVerified\" TIMESTAMPTZ,\n  image TEXT,\n\n  PRIMARY KEY (id)\n);\n\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/pouchdb.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/pouchdb.svg\" width=\"64\" height=\"64\" />\n\n# PouchDB Adapter\n\n## Resources\n\n- [PouchDB documentation](https://pouchdb.com/api.html)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install pouchdb pouchdb-find @auth/pouchdb-adapter\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { PouchDBAdapter } from \"@auth/pouchdb-adapter\"\nimport PouchDB from \"pouchdb\"\n\n// Setup your PouchDB instance and database\nPouchDB.plugin(require(\"pouchdb-adapter-leveldb\")) // Or any other adapter\n  .plugin(require(\"pouchdb-find\")) // Don't forget the `pouchdb-find` plugin\n\nconst pouchdb = new PouchDB(\"auth_db\", { adapter: \"leveldb\" })\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [],\n  adapter: PouchDBAdapter(pouchdb),\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { PouchDBAdapter } from \"@auth/pouchdb-adapter\"\nimport PouchDB from \"pouchdb\"\n\n// Setup your PouchDB instance and database\nPouchDB.plugin(require(\"pouchdb-adapter-leveldb\")) // Or any other adapter\n  .plugin(require(\"pouchdb-find\")) // Don't forget the `pouchdb-find` plugin\n\nconst pouchdb = new PouchDB(\"auth_db\", { adapter: \"leveldb\" })\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: PouchDBAdapter(pouchdb),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { PouchDBAdapter } from \"@auth/pouchdb-adapter\"\nimport PouchDB from \"pouchdb\"\n\n// Setup your PouchDB instance and database\nPouchDB.plugin(require(\"pouchdb-adapter-leveldb\")) // Or any other adapter\n  .plugin(require(\"pouchdb-find\")) // Don't forget the `pouchdb-find` plugin\n\nconst pouchdb = new PouchDB(\"auth_db\", { adapter: \"leveldb\" })\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [],\n  adapter: PouchDBAdapter(pouchdb),\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { PouchDBAdapter } from \"@auth/pouchdb-adapter\"\nimport PouchDB from \"pouchdb\"\n\n// Setup your PouchDB instance and database\nPouchDB.plugin(require(\"pouchdb-adapter-leveldb\")) // Or any other adapter\n  .plugin(require(\"pouchdb-find\")) // Don't forget the `pouchdb-find` plugin\n\nconst pouchdb = new PouchDB(\"auth_db\", { adapter: \"leveldb\" })\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: PouchDBAdapter(pouchdb),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n<Callout>\n  Depending on your architecture you can use PouchDB's http adapter to reach any\n  database compliant with the CouchDB protocol (CouchDB, Cloudant, etc.) or use\n  any other PouchDB compatible adapter (leveldb, in-memory, etc.)\n</Callout>\n\n<Callout type=\"info\">\n  Your PouchDB instance MUST provide the `pouchdb-find` plugin since it is used\n  internally by the adapter to build and manage indexes\n</Callout>\n\n### Advanced usage\n\n#### Memory-First Caching Strategy\n\nIf you need to boost your authentication layer performance, you may use PouchDB's powerful sync features and various adapters, to build a memory-first caching strategy.\n\nUse an in-memory PouchDB as your main authentication database, and synchronize it with any other persisted PouchDB. You may do a one way, one-off replication at startup from the persisted PouchDB into the in-memory PouchDB, then two-way, continuous sync.\n\nThis will most likely not increase performance much in a serverless environment due to various reasons such as concurrency, function startup time increases, etc.\n\nFor more details, please see https://pouchdb.com/api.html#sync\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/prisma.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\nimport { Accordion, Accordions } from \"@/components/Accordion\"\n\n<img align=\"right\" src=\"/img/adapters/prisma.svg\" width=\"64\" height=\"64\" />\n\n# Prisma Adapter\n\n## Resources\n\n- [Prisma documentation](https://www.prisma.io/docs)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @prisma/client @prisma/extension-accelerate @auth/prisma-adapter\nnpm install prisma --save-dev\n```\n\n### Environment Variables\n\nIf you're using Prisma Postgres, the `DATABASE_URL` will be automatically set up during initialization. For other databases, you'll need to manually configure the `DATABASE_URL` environment variable. For more information, read the [docs](https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/relational-databases/connect-your-database-typescript-postgresql).\n\n```sh\nDATABASE_URL=postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA\n```\n\n### Configuration\n\nFirst, initialize Prisma in your project. If you're using Prisma Postgres, run:\n\n```bash\nnpx prisma init --db --output ./src/generated/prisma\n```\n\nThis will create a Prisma Postgres database, set up your schema file, and configure the output directory for the generated Prisma Client.\n\nFor other databases, run:\n\n```bash\nnpx prisma init --output ./src/generated/prisma\n```\n\nThen manually configure your `DATABASE_URL` in the `.env` file.\n\nTo improve performance using `Prisma ORM`, we can set up the Prisma instance to ensure only one instance is created throughout the project and then import it from any file as needed. This approach avoids recreating instances of PrismaClient every time it is used. Finally, we can import the Prisma instance from the `auth.ts` file configuration.\n\n```ts filename=\"prisma.ts\"\nimport { PrismaClient } from \"../src/generated/client\"\nimport { withAccelerate } from \"@prisma/extension-accelerate\"\n\nconst globalForPrisma = globalThis as unknown as { prisma: PrismaClient }\n\nexport const prisma =\n  globalForPrisma.prisma || new PrismaClient().$extends(withAccelerate())\n\nif (process.env.NODE_ENV !== \"production\") globalForPrisma.prisma = prisma\n```\n\n<Callout type=\"info\">\n  If you're not using Prisma Postgres with Accelerate, you can omit the\n  `withAccelerate()` extension and delete `.$extends(withAccelerate())`.\n</Callout>\n\n<Callout type=\"warning\">\n  We recommend using version `@prisma/client@5.12.0` or above if using proxy (or\n  middleware in older Next.js versions) or any other edge runtime(s). In Next.js\n  16+, `proxy.ts` runs on the Node.js runtime, so this may no longer be\n  necessary. See [edge compatibility](#edge-compatibility) below for more\n  information.\n</Callout>\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { PrismaAdapter } from \"@auth/prisma-adapter\"\nimport { prisma } from \"@/prisma\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: PrismaAdapter(prisma),\n  providers: [],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { PrismaAdapter } from \"@auth/prisma-adapter\"\nimport { prisma } from \"@/prisma\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: PrismaAdapter(prisma),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { PrismaAdapter } from \"@auth/prisma-adapter\"\nimport { prisma } from \"@/prisma\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: PrismaAdapter(prisma),\n  providers: [],\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { PrismaAdapter } from \"@auth/prisma-adapter\"\nimport { prisma } from \"@/prisma\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: PrismaAdapter(prisma),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Edge Compatibility\n\nPrisma has shipped edge runtime support for their client in version `5.12.0`. You can read more about it on their [edge documentation](https://www.prisma.io/docs/orm/prisma-client/deployment/edge/overview). This requires specific database drivers and therefore is only compatible with certain database types / hosting providers. Check their [list of supported drivers](https://www.prisma.io/docs/orm/prisma-client/deployment/edge/overview#which-database-drivers-are-edge-compatible) before getting started. You can check out an example Auth.js application with `next-auth` and Prisma on the edge [here](https://github.com/ndom91/authjs-prisma-edge-example).\n\nFor more about edge compatibility in general, check out our [edge compatibility guide](/guides/edge-compatibility).\n\nThe original database edge-runtime workaround, to split your `auth.ts` configuration into two, will be kept below.\n\n#### Old Edge Workaround\n\nAt the moment, Prisma is still working on being fully compatible with edge runtimes like Vercel's. See the issue being tracked [here](https://github.com/prisma/prisma/issues/20560), and Prisma's announcement about early edge support in the `5.9.1` [changelog](https://github.com/prisma/prisma/releases/tag/5.9.0). There are two options to deal with this issue:\n\n- Use the Prisma's [Accelerate](https://pris.ly/d/accelerate) feature\n- Follow our [Edge Compatibility](/guides/edge-compatibility) page as the workaround. This uses the `jwt` session strategy and separates the `auth.ts` configuration into two files.\n\nUsing Prisma with the `jwt` session strategy and `@prisma/client@5.9.1` or above doesn't require any additional modifications, other than ensuring you don't do any database queries in your proxy (or middleware in older Next.js versions).\n\nSince `@prisma/client@5.9.1`, Prisma no longer throws about being incompatible with the edge runtime at instantiation, but at query time. Therefore, it is possible to import it in files being used in your proxy as long as you do not execute any queries in your proxy.\n\n### Schema\n\nYou need to use at least Prisma `2.26.0`. Create a schema file at `prisma/schema.prisma` with the following models.\n\n<Accordions>\n<Accordion title=\"PostgreSQL\">\n\n```prisma filename=\"prisma/schema-postgres.prisma\"\ndatasource db {\n  provider = \"postgresql\"\n  url      = env(\"DATABASE_URL\")\n}\n\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../src/generated/prisma\"\n}\n\nmodel User {\n  id            String          @id @default(cuid())\n  name          String?\n  email         String          @unique\n  emailVerified DateTime?\n  image         String?\n  accounts      Account[]\n  sessions      Session[]\n  // Optional for WebAuthn support\n  Authenticator Authenticator[]\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n}\n\nmodel Account {\n  userId            String\n  type              String\n  provider          String\n  providerAccountId String\n  refresh_token     String?\n  access_token      String?\n  expires_at        Int?\n  token_type        String?\n  scope             String?\n  id_token          String?\n  session_state     String?\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@id([provider, providerAccountId])\n}\n\nmodel Session {\n  sessionToken String   @unique\n  userId       String\n  expires      DateTime\n  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n}\n\nmodel VerificationToken {\n  identifier String\n  token      String\n  expires    DateTime\n\n  @@id([identifier, token])\n}\n\n// Optional for WebAuthn support\nmodel Authenticator {\n  credentialID         String  @unique\n  userId               String\n  providerAccountId    String\n  credentialPublicKey  String\n  counter              Int\n  credentialDeviceType String\n  credentialBackedUp   Boolean\n  transports           String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@id([userId, credentialID])\n}\n```\n\n</Accordion>\n<Accordion title=\"MySQL\">\n\n```prisma filename=\"prisma/schema-mysql.prisma\"\ndatasource db {\n  provider = \"mysql\"\n  url      = env(\"DATABASE_URL\")\n}\n\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../src/generated/prisma\"\n}\n\nmodel User {\n  id            String          @id @default(cuid())\n  name          String?\n  username      String?         @unique\n  email         String?         @unique\n  emailVerified DateTime?\n  image         String?\n  accounts      Account[]\n  sessions      Session[]\n  // Optional for WebAuthn support\n  Authenticator Authenticator[]\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n}\n\nmodel Account {\n  id                       String  @id @default(cuid())\n  userId                   String  @unique\n  type                     String\n  provider                 String\n  providerAccountId        String\n  refresh_token            String? @db.Text\n  access_token             String? @db.Text\n  expires_at               Int?\n  token_type               String?\n  scope                    String?\n  id_token                 String? @db.Text\n  session_state            String?\n  refresh_token_expires_in Int?\n  user                     User?   @relation(fields: [userId], references: [id])\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n\n  @@unique([provider, providerAccountId])\n  @@index([userId])\n}\n\nmodel Session {\n  id           String   @id @default(cuid())\n  sessionToken String   @unique\n  userId       String\n  expires      DateTime\n  user         User     @relation(fields: [userId], references: [id])\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n\n  @@index([userId])\n}\n\nmodel VerificationToken {\n  identifier String\n  token      String\n  expires    DateTime\n\n  @@unique([identifier, token])\n}\n\n// Optional for WebAuthn support\nmodel Authenticator {\n  credentialID         String  @unique\n  userId               String\n  providerAccountId    String\n  credentialPublicKey  String\n  counter              Int\n  credentialDeviceType String\n  credentialBackedUp   Boolean\n  transports           String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@id([userId, credentialID])\n}\n```\n\n<Callout>\n  When using the MySQL connector for Prisma, the [Prisma `String`\n  type](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#string)\n  gets mapped to `varchar(191)` which may not be long enough to store fields\n  such as `id_token` in the `Account` model. This can be avoided by explicitly\n  using the `Text` type with `@db.Text` as shown for some fields in the example\n  above.\n</Callout>\n\n</Accordion>\n<Accordion title=\"SQLite\">\n\n```prisma filename=\"prisma/schema-sqlite.prisma\"\ndatasource db {\n  provider = \"sqlite\"\n  url      = env(\"DATABASE_URL\")\n}\n\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../src/generated/prisma\"\n}\n\nmodel User {\n  id            String          @id @default(cuid())\n  name          String?\n  email         String?         @unique\n  emailVerified DateTime?\n  image         String?\n  accounts      Account[]\n  sessions      Session[]\n  // Optional for WebAuthn support\n  Authenticator Authenticator[]\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n}\n\nmodel Account {\n  id                String  @id @default(cuid())\n  userId            String\n  type              String\n  provider          String\n  providerAccountId String\n  refresh_token     String?\n  access_token      String?\n  expires_at        Int?\n  token_type        String?\n  scope             String?\n  id_token          String?\n  session_state     String?\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@unique([provider, providerAccountId])\n}\n\nmodel Session {\n  id           String   @id @default(cuid())\n  sessionToken String   @unique\n  userId       String\n  expires      DateTime\n  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n}\n\nmodel VerificationToken {\n  identifier String\n  token      String\n  expires    DateTime\n\n  @@unique([identifier, token])\n}\n\n// Optional for WebAuthn support\nmodel Authenticator {\n  credentialID         String  @unique\n  userId               String\n  providerAccountId    String\n  credentialPublicKey  String\n  counter              Int\n  credentialDeviceType String\n  credentialBackedUp   Boolean\n  transports           String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@id([userId, credentialID])\n}\n```\n\n</Accordion>\n<Accordion title=\"MongoDB\">\n\n```prisma filename=\"prisma/schema-mongodb.prisma\"\ndatasource db {\n  provider = \"mongodb\"\n  url      = env(\"DATABASE_URL\")\n}\n\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../src/generated/prisma\"\n}\n\nmodel User {\n  id            String          @id @default(auto()) @map(\"_id\") @db.ObjectId\n  name          String?\n  email         String?         @unique\n  emailVerified DateTime?\n  image         String?\n  accounts      Account[]\n  sessions      Session[]\n  // Optional for WebAuthn support\n  Authenticator Authenticator[]\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n}\n\nmodel Account {\n  id                String  @id @default(auto()) @map(\"_id\") @db.ObjectId\n  userId            String  @db.ObjectId\n  type              String\n  provider          String\n  providerAccountId String\n  refresh_token     String? @db.String\n  access_token      String? @db.String\n  expires_at        Int?\n  token_type        String?\n  scope             String?\n  id_token          String? @db.String\n  session_state     String?\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@unique([provider, providerAccountId])\n}\n\nmodel Session {\n  id           String   @id @default(auto()) @map(\"_id\") @db.ObjectId\n  sessionToken String   @unique\n  userId       String   @db.ObjectId\n  expires      DateTime\n  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n}\n\nmodel VerificationToken {\n  id         String   @id @default(auto()) @map(\"_id\") @db.ObjectId\n  identifier String\n  token      String\n  expires    DateTime\n\n  @@unique([identifier, token])\n}\n\n// Optional for WebAuthn support\nmodel Authenticator {\n  credentialID         String  @id @map(\"_id\")\n  userId               String  @db.ObjectId\n  providerAccountId    String\n  credentialPublicKey  String\n  counter              Int\n  credentialDeviceType String\n  credentialBackedUp   Boolean\n  transports           String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@unique([userId, credentialID])\n}\n```\n\nPrisma supports MongoDB, and so does Auth.js. Following the instructions of the [Prisma documentation](https://www.prisma.io/docs/concepts/database-connectors/mongodb) on the MongoDB connector, things to look out for include the following.\n\n1. Make sure that the id fields are mapped correctly\n\n```prisma\nid  String  @id @default(auto()) @map(\"_id\") @db.ObjectId\n```\n\n2. Use the native database type attributes like `@db.String` and for Id fields, `@db.ObjectId`.\n\n```prisma\nuser_id            String   @db.ObjectId\nrefresh_token      String?  @db.String\naccess_token       String?  @db.String\nid_token           String?  @db.String\n```\n\nThis has all been applied in the above example schema already.\n\n</Accordion>\n</Accordions>\n\n### Apply Schema\n\nThis will create an SQL migration file and execute it:\n\n```bash npm2yarn\nnpm exec prisma migrate dev\n```\n\nNote that you will need to specify your database connection string in the environment variable `DATABASE_URL`. You can do this by setting it in a `.env` file at the root of your project.\n\n### Generate Prisma Client\n\n`prisma migrate dev` will also generate the Prisma client, but if you need to generate it again manually you can run the following command.\n\n```bash npm2yarn\nnpm exec prisma generate\n```\n\n### Development Workflow\n\nWhen you're working on your application and making changes to your database schema, you'll need to\nrun the migrate command again every time you make changes to the schema in order for Prisma to (1) generate a migration file and apply it to the underlying database and (2) regenerate the Prisma client in your project with the latest types and model methods.\n\n```bash npm2yarn\nnpm exec prisma migrate dev\n```\n\n### Naming Conventions\n\nIf mixed `snake_case` and `camelCase` column names is an issue for you and/or your underlying database system, we recommend using Prisma's [`@map()` feature](https://www.prisma.io/docs/concepts/components/prisma-schema/names-in-underlying-database) to change the field names. This won't affect Auth.js, but will allow you to customize the column names to whichever naming convention you prefer.\n\nFor example, moving to `snake_case` and plural table names.\n\n```prisma filename=\"schema.prisma\"\nmodel Account {\n  id                 String  @id @default(cuid())\n  userId             String  @map(\"user_id\")\n  type               String\n  provider           String\n  providerAccountId  String  @map(\"provider_account_id\")\n  refresh_token      String? @db.Text\n  access_token       String? @db.Text\n  expires_at         Int?\n  token_type         String?\n  scope              String?\n  id_token           String? @db.Text\n  session_state      String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@unique([provider, providerAccountId])\n  @@map(\"accounts\")\n}\n\nmodel Session {\n  id           String   @id @default(cuid())\n  sessionToken String   @unique @map(\"session_token\")\n  userId       String   @map(\"user_id\")\n  expires      DateTime\n  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@map(\"sessions\")\n}\n\nmodel User {\n  id            String    @id @default(cuid())\n  name          String?\n  email         String?   @unique\n  emailVerified DateTime? @map(\"email_verified\")\n  image         String?\n  accounts      Account[]\n  sessions      Session[]\n\n  @@map(\"users\")\n}\n\nmodel VerificationToken {\n  identifier String\n  token      String\n  expires    DateTime\n\n  @@unique([identifier, token])\n  @@map(\"verification_tokens\")\n}\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/sequelize.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/sequelize.svg\" height=\"64\" width=\"64\" />\n\n# Sequelize Adapter\n\n## Resources\n\n- [Sequelize documentation](https://sequelize.org/docs/v6/getting-started/)\n- [Connecting to a Database](https://sequelize.org/master/manual/getting-started.html#connecting-to-a-database)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/sequelize-adapter sequelize\n```\n\n### Environment Variables\n\n```sh\nDATABASE_URL=postgres://postgres:adminadmin@0.0.0.0:5432/db\n```\n\n### Configuration\n\n<Callout type=\"warning\">\n  You'll also have to manually install [the driver for your\n  database](https://sequelize.org/master/manual/getting-started.html) of choice.\n</Callout>\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport SequelizeAdapter from \"@auth/sequelize-adapter\"\nimport { Sequelize } from \"sequelize\"\n\nconst sequelize = new Sequelize(process.env.DATABASE_URL)\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [],\n  adapter: SequelizeAdapter(sequelize),\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport SequelizeAdapter from \"@auth/sequelize-adapter\"\nimport { Sequelize } from \"sequelize\"\n\nconst sequelize = new Sequelize(import.meta.env.DATABASE_URL)\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: SequelizeAdapter(sequelize),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport SequelizeAdapter from \"@auth/sequelize-adapter\"\nimport { Sequelize } from \"sequelize\"\n\nconst sequelize = new Sequelize(process.env.DATABASE_URL)\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [],\n  adapter: SequelizeAdapter(sequelize),\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport SequelizeAdapter from \"@auth/sequelize-adapter\"\nimport { Sequelize } from \"sequelize\"\n\nconst sequelize = new Sequelize(process.env.DATABASE_URL)\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: SequelizeAdapter(sequelize),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Schema\n\nBy default, the sequelize adapter will not create tables in your database. In production, best practice is to create the [required tables](/concepts/database-models) in your database via [migrations](https://sequelize.org/master/manual/migrations.html). In development, you are able to call [`sequelize.sync()`](https://sequelize.org/master/manual/model-basics.html#model-synchronization) to have sequelize create the necessary tables, foreign keys and indexes:\n\n> This schema is adapted for use in Sequelize and based upon our main\n> [schema](/concepts/database-models)\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport SequelizeAdapter from \"@auth/sequelize-adapter\"\nimport Sequelize from \"sequelize\"\n\nconst sequelize = new Sequelize(\"sqlite::memory:\")\nconst adapter = SequelizeAdapter(sequelize)\n\n// Calling sync() is not recommended in production\nsequelize.sync()\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter,\n})\n```\n\n## Advanced usage\n\n### Using custom models\n\nSequelize models are option to customization like so:\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport SequelizeAdapter, { models } from \"@auth/sequelize-adapter\"\nimport Sequelize, { DataTypes } from \"sequelize\"\n\nconst sequelize = new Sequelize(\"sqlite::memory:\")\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  // https://authjs.dev/reference/providers/\n  providers: [],\n  adapter: SequelizeAdapter(sequelize, {\n    models: {\n      User: sequelize.define(\"user\", {\n        ...models.User,\n        phoneNumber: DataTypes.STRING,\n      }),\n    },\n  }),\n})\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/supabase.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/supabase.svg\" width=\"64\" height=\"64\" />\n\n# Supabase Adapter\n\n## Resources\n\n- [Supabase documentation](https://supabase.com/docs)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @supabase/supabase-js @auth/supabase-adapter\n```\n\n### Environment Variables\n\n```sh\nSUPABASE_URL\nSUPABASE_SERVICE_ROLE_KEY\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { SupabaseAdapter } from \"@auth/supabase-adapter\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [],\n  adapter: SupabaseAdapter({\n    url: process.env.SUPABASE_URL,\n    secret: process.env.SUPABASE_SERVICE_ROLE_KEY,\n  }),\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { SupabaseAdapter } from \"@auth/supabase-adapter\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: SupabaseAdapter({\n      url: import.meta.env.SUPABASE_URL,\n      secret: import.meta.env.SUPABASE_SERVICE_ROLE_KEY,\n    }),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { SupabaseAdapter } from \"@auth/supabase-adapter\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [],\n  adapter: SupabaseAdapter({\n    url: process.env.SUPABASE_URL,\n    secret: process.env.SUPABASE_SERVICE_ROLE_KEY,\n  }),\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { SupabaseAdapter } from \"@auth/supabase-adapter\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: SupabaseAdapter({\n      url: process.env.SUPABASE_URL,\n      secret: process.env.SUPABASE_SERVICE_ROLE_KEY,\n    }),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n<Callout>\nThis adapter is developed by the community and not officially maintained or supported by Supabase. It uses the Supabase Database to store user and session data in a separate `next_auth` schema. It is a standalone Auth server that does not interface with Supabase Auth and therefore provides a different feature set.\n\nIf you're looking for an officially maintained Auth server with additional features like [built-in email server](https://supabase.com/docs/guides/auth/auth-email#configure-email-settings?utm_source=authjs-docs&medium=referral&campaign=authjs), [phone auth](https://supabase.com/docs/guides/auth/auth-twilio?utm_source=authjs-docs&medium=referral&campaign=authjs), and [Multi Factor Authentication (MFA / 2FA)](https://supabase.com/contact/mfa?utm_source=authjs-docs&medium=referral&campaign=authjs), please use [Supabase Auth](https://supabase.com/auth) with the [Auth Helpers for Next.js](https://supabase.com/docs/guides/auth/auth-helpers/nextjs?utm_source=authjs-docs&medium=referral&campaign=authjs).\n\n</Callout>\n\n### Schema\n\nSetup your database as described in our main [schema](https://authjs.dev/reference/core/adapters#models), by copying the SQL schema below in the Supabase [SQL Editor](https://app.supabase.com/project/_/sql).\n\nAlternatively you can select the NextAuth Quickstart card on the [SQL Editor page](https://app.supabase.com/project/_/sql), or [create a migration with the Supabase CLI](https://supabase.com/docs/guides/cli/local-development#database-migrations?utm_source=authjs-docs&medium=referral&campaign=authjs).\n\n```sql\n--\n-- Name: next_auth; Type: SCHEMA;\n--\nCREATE SCHEMA next_auth;\n\nGRANT USAGE ON SCHEMA next_auth TO service_role;\nGRANT ALL ON SCHEMA next_auth TO postgres;\n\n--\n-- Create users table\n--\nCREATE TABLE IF NOT EXISTS next_auth.users\n(\n    id uuid NOT NULL DEFAULT uuid_generate_v4(),\n    name text,\n    email text,\n    \"emailVerified\" timestamp with time zone,\n    image text,\n    CONSTRAINT users_pkey PRIMARY KEY (id),\n    CONSTRAINT email_unique UNIQUE (email)\n);\n\nGRANT ALL ON TABLE next_auth.users TO postgres;\nGRANT ALL ON TABLE next_auth.users TO service_role;\n\n--- uid() function to be used in RLS policies\nCREATE FUNCTION next_auth.uid() RETURNS uuid\n    LANGUAGE sql STABLE\n    AS $$\n  select\n  \tcoalesce(\n\t\tnullif(current_setting('request.jwt.claim.sub', true), ''),\n\t\t(nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'sub')\n\t)::uuid\n$$;\n\n--\n-- Create sessions table\n--\nCREATE TABLE IF NOT EXISTS  next_auth.sessions\n(\n    id uuid NOT NULL DEFAULT uuid_generate_v4(),\n    expires timestamp with time zone NOT NULL,\n    \"sessionToken\" text NOT NULL,\n    \"userId\" uuid,\n    CONSTRAINT sessions_pkey PRIMARY KEY (id),\n    CONSTRAINT sessionToken_unique UNIQUE (\"sessionToken\"),\n    CONSTRAINT \"sessions_userId_fkey\" FOREIGN KEY (\"userId\")\n        REFERENCES  next_auth.users (id) MATCH SIMPLE\n        ON UPDATE NO ACTION\n        ON DELETE CASCADE\n);\n\nGRANT ALL ON TABLE next_auth.sessions TO postgres;\nGRANT ALL ON TABLE next_auth.sessions TO service_role;\n\n--\n-- Create accounts table\n--\nCREATE TABLE IF NOT EXISTS  next_auth.accounts\n(\n    id uuid NOT NULL DEFAULT uuid_generate_v4(),\n    type text NOT NULL,\n    provider text NOT NULL,\n    \"providerAccountId\" text NOT NULL,\n    refresh_token text,\n    access_token text,\n    expires_at bigint,\n    token_type text,\n    scope text,\n    id_token text,\n    session_state text,\n    oauth_token_secret text,\n    oauth_token text,\n    \"userId\" uuid,\n    CONSTRAINT accounts_pkey PRIMARY KEY (id),\n    CONSTRAINT provider_unique UNIQUE (provider, \"providerAccountId\"),\n    CONSTRAINT \"accounts_userId_fkey\" FOREIGN KEY (\"userId\")\n        REFERENCES  next_auth.users (id) MATCH SIMPLE\n        ON UPDATE NO ACTION\n        ON DELETE CASCADE\n);\n\nGRANT ALL ON TABLE next_auth.accounts TO postgres;\nGRANT ALL ON TABLE next_auth.accounts TO service_role;\n\n--\n-- Create verification_tokens table\n--\nCREATE TABLE IF NOT EXISTS  next_auth.verification_tokens\n(\n    identifier text,\n    token text,\n    expires timestamp with time zone NOT NULL,\n    CONSTRAINT verification_tokens_pkey PRIMARY KEY (token),\n    CONSTRAINT token_unique UNIQUE (token),\n    CONSTRAINT token_identifier_unique UNIQUE (token, identifier)\n);\n\nGRANT ALL ON TABLE next_auth.verification_tokens TO postgres;\nGRANT ALL ON TABLE next_auth.verification_tokens TO service_role;\n```\n\n### Expose the NextAuth schema in Supabase\n\nExpose the `next_auth` schema via the Serverless API in the [API settings](https://app.supabase.com/project/_/settings/api) by adding `next_auth` to the \"Exposed schemas\" list.\n\nWhen developing locally add `next_auth` to the `schemas` array in the `config.toml` file in the `supabase` folder that was generated by the [Supabase CLI](https://supabase.com/docs/guides/cli/local-development#initialize-your-project?utm_source=authjs-docs&medium=referral&campaign=authjs).\n\n## Advanced usage\n\n### Enabling Row Level Security (RLS)\n\nPostgres provides a powerful feature called [Row Level Security (RLS)](https://supabase.com/docs/guides/auth/row-level-security?utm_source=authjs-docs&medium=referral&campaign=authjs) to limit access to data.\n\nThis works by sending a signed JWT to your [Supabase Serverless API](https://supabase.com/docs/guides/api?utm_source=authjs-docs&medium=referral&campaign=authjs). There is two steps to make this work with NextAuth:\n\n#### Generate the Supabase `access_token` JWT in the session callback\n\nTo sign the JWT use the `jsonwebtoken` package:\n\n```bash npm2yarn\nnpm install jsonwebtoken\n```\n\nUsing the [session callback](/reference/core/types#session) create the Supabase `access_token` and append it to the `session` object.\n\nTo sign the JWT use the Supabase JWT secret which can be found in the [API settings](https://app.supabase.com/project/_/settings/api)\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { SupabaseAdapter } from \"@auth/supabase-adapter\"\nimport jwt from \"jsonwebtoken\"\n\n// For more information on each option (and a full list of options) go to\n// https://authjs.dev/reference/core/types#authconfig\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  // https://authjs.dev/getting-started/authentication/oauth\n  providers: [],\n  adapter: SupabaseAdapter({\n    url: process.env.NEXT_PUBLIC_SUPABASE_URL,\n    secret: process.env.SUPABASE_SERVICE_ROLE_KEY,\n  }),\n  callbacks: {\n    async session({ session, user }) {\n      const signingSecret = process.env.SUPABASE_JWT_SECRET\n      if (signingSecret) {\n        const payload = {\n          aud: \"authenticated\",\n          exp: Math.floor(new Date(session.expires).getTime() / 1000),\n          sub: user.id,\n          email: user.email,\n          role: \"authenticated\",\n        }\n        session.supabaseAccessToken = jwt.sign(payload, signingSecret)\n      }\n      return session\n    },\n  },\n})\n```\n\n#### Inject the Supabase `access_token` JWT into the client\n\nFor example, given the following public schema:\n\n```sql\n-- Note: This table contains user data. Users should only be able to view and update their own data.\ncreate table users (\n  -- UUID from next_auth.users\n  id uuid not null primary key,\n  name text,\n  email text,\n  image text,\n  constraint \"users_id_fkey\" foreign key (\"id\")\n        references  next_auth.users (id) match simple\n        on update no action\n        on delete cascade -- if a user is deleted in NextAuth they will also be deleted in our public table.\n);\nalter table users enable row level security;\ncreate policy \"Can view own user data.\" on users for select using (next_auth.uid() = id);\ncreate policy \"Can update own user data.\" on users for update using (next_auth.uid() = id);\n\n-- This trigger automatically creates a user entry when a new user signs up via NextAuth.\ncreate function public.handle_new_user()\nreturns trigger as $$\nbegin\n  insert into public.users (id, name, email, image)\n  values (new.id, new.name, new.email, new.image);\n  return new;\nend;\n$$ language plpgsql security definer;\ncreate trigger on_auth_user_created\n  after insert on next_auth.users\n  for each row execute procedure public.handle_new_user();\n```\n\nThe `supabaseAccessToken` is now available on the `session` object and can be passed to the supabase-js client. This works in any environment: client-side, server-side (API routes, SSR), as well as in middleware edge functions!\n\n```js\n// Use `useSession()` or `unstable_getServerSession()` to get the NextAuth session.\n\nconst { supabaseAccessToken } = session\n\nconst supabase = createClient(\n  process.env.NEXT_PUBLIC_SUPABASE_URL,\n  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,\n  {\n    global: {\n      headers: {\n        Authorization: `Bearer ${supabaseAccessToken}`,\n      },\n    },\n  }\n)\n// Now you can query with RLS enabled.\nconst { data, error } = await supabase.from(\"users\").select(\"*\")\n```\n\n### TypeScript\n\nYou can pass types that were generated with the Supabase CLI to the Supabase Client to get enhanced type safety and auto-completion.\n\nCreating a new supabase client object:\n\n```ts\nimport { createClient } from \"@supabase/supabase-js\"\nimport { Database } from \"../database.types\"\n\nconst supabase = createClient<Database>()\n```\n\n#### Extend the session type with the `supabaseAccessToken`\n\nIn order to extend the `session` object with the `supabaseAccessToken` we need to extend the `session` interface in a `types/next-auth.d.ts` file:\n\n```ts filename=\"types/next-auth.d.ts\"\nimport NextAuth, { type DefaultSession } from \"next-auth\"\n\ndeclare module \"next-auth\" {\n  // Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context\n  interface Session {\n    // A JWT which can be used as Authorization header with supabase-js for RLS.\n    supabaseAccessToken?: string\n    user: {\n      // The user's postal address\n      address: string\n    } & DefaultSession[\"user\"]\n  }\n}\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/surrealdb.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/surrealdb.svg\" height=\"64\" width=\"64\" />\n\n# SurrealDB Adapter\n\n## Resources\n\n- [SurrealDB documentation](https://www.surrealdb.com/docs)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/surrealdb-adapter surrealdb\n```\n\n### Environment Variables\n\nA valid authentication combination must be provided. The following authentication combinations are supported:\n\n- RootAuth\n- NamespaceAuth\n- DatabaseAuth\n- ScopeAuth\n\n```sh\nAUTH_SURREAL_URL (required)\nAUTH_SURREAL_NS\nAUTH_SURREAL_DB\nAUTH_SURREAL_USER\nAUTH_SURREAL_PW\nAUTH_SURREAL_SCOPE\nSURREAL_NS (required when using RootAuth or NamespaceAuth)\nSURREAL_DB (required when using RootAuth or NamespaceAuth)\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { SurrealDBAdapter } from \"@auth/surrealdb-adapter\"\nimport clientPromise from \"./lib/surrealdb\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [],\n  adapter: SurrealDBAdapter(clientPromise),\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { SurrealDBAdapter } from \"@auth/surrealdb-adapter\"\nimport clientPromise from \"./lib/surrealdb\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: SurrealDBAdapter(clientPromise),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { SurrealDBAdapter } from \"@auth/surrealdb-adapter\"\nimport clientPromise from \"./lib/surrealdb\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [],\n  adapter: SurrealDBAdapter(clientPromise),\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { SurrealDBAdapter } from \"@auth/surrealdb-adapter\"\nimport clientPromise from \"./lib/surrealdb\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: SurrealDBAdapter(clientPromise),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\nThe SurrealDB adapter does not handle connections automatically, so you will have to make sure that you pass the Adapter a `SurrealDBClient` that is connected already. Below you can see an example how to do this.\n\n### Utils File\n\n```ts filename=\"./lib/surrealdb_utils.ts\"\nimport type { ConnectOptions, AnyAuth } from \"surrealdb\"\nimport { Surreal, ConnectionStatus } from \"surrealdb\"\n\n/**\n * Maintains a single instance of surrealdb.\n * Automatically reconnects unless options.reconnect is set to false manually.\n */\nexport class MySurreal {\n  // A single instantiation of a surreal connection\n  private _surrealdb: Surreal | undefined\n  private _url: string | URL\n  private _opts: ConnectOptions | undefined\n\n  constructor(\n    url: Parameters<InstanceType<typeof Surreal>[\"connect\"]>[0],\n    opts?: ConnectOptions\n  ) {\n    this._url = url\n    if (opts && opts.reconnect === undefined) {\n      opts.reconnect = true\n    }\n    this._opts = opts\n  }\n\n  async surrealdb(): Promise<Surreal> {\n    // Init Surreal\n    if (!this._surrealdb) {\n      this._surrealdb = new Surreal()\n    }\n\n    if (this._surrealdb.status == ConnectionStatus.Connected) {\n      return this._surrealdb\n    } else if (this._surrealdb.status == ConnectionStatus.Disconnected) {\n      try {\n        // Connect as a database user\n        await this._surrealdb.connect(this._url, this._opts)\n        if (process.env.NODE_ENV === \"development\") {\n          const str = this.toConnectionString(\n            this._surrealdb.status,\n            this._opts\n          )\n          console.info(str)\n        }\n      } catch (error) {\n        if (error instanceof Error) throw error\n        throw new Error(error as unknown as string)\n      }\n    }\n    return this._surrealdb\n  }\n\n  private toConnectionString(status: ConnectionStatus, opts?: ConnectOptions) {\n    let str = `${status}`\n    const auth = opts?.auth\n\n    if (auth && typeof auth !== \"string\") {\n      if (\"username\" in auth) {\n        str += ` as ${auth.username}`\n      }\n      if (\"database\" in auth && \"namespace\" in auth) {\n        str += ` for ${auth.namespace} ${auth.database}`\n      } else if (\"namespace\" in auth) {\n        str += ` for ${auth.namespace}`\n      } else if (\"database\" in auth) {\n        str += ` for ${auth.database}`\n      }\n    }\n    return str\n  }\n}\n\n/**\n * Converts environment variables to an AnyAuth type\n * to connect with the database\n * @param param0 - environment variables\n * @returns {RootAuth | NamespaceAuth | DatabaseAuth | ScopeAuth}\n */\nexport function toAnyAuth({\n  username,\n  password,\n  namespace,\n  database,\n  scope,\n}: Record<string, string | undefined>) {\n  let auth: AnyAuth\n  if (username && password && namespace && database) {\n    auth = {\n      database,\n      namespace,\n      username,\n      password,\n    }\n  } else if (username && password && namespace) {\n    auth = {\n      namespace,\n      username,\n      password,\n    }\n  } else if (username && password) {\n    auth = {\n      username,\n      password,\n    }\n  } else if (scope) {\n    auth = {\n      namespace,\n      database,\n      username,\n      password,\n      scope,\n    }\n  } else {\n    throw new Error(\"unsupported any auth configuration\")\n  }\n  return auth\n}\n```\n\n### Authorization\n\nThe clientPromise provides a connection to the database. You could use any connect option you wish. For quick setup, use the DatabaseAuth method. For best security, we recommend creating a Record Access method if you know how to properly setup access table permissions.\n\n```ts filename=\"./lib/surrealdb.ts\"\nimport { type Surreal } from \"surrealdb\"\nimport { handleDisconnect, MySurreal, toAnyAuth } from \"./lib/surrealdb_utils\"\n\nconst clientPromise = new Promise<Surreal>(async (resolve, reject) => {\n  try {\n    const {\n      AUTH_SURREAL_URL: auth_url,\n      AUTH_SURREAL_NS: auth_ns,\n      AUTH_SURREAL_DB: auth_db,\n      AUTH_SURREAL_USER: auth_user,\n      AUTH_SURREAL_PW: auth_pw,\n      AUTH_SURREAL_SCOPE: auth_scope,\n      SURREAL_NS: namespace,\n      SURREAL_DB: database,\n    } = process.env\n    if (!auth_url) throw new Error(\"required auth_url\")\n    const auth = toAnyAuth({\n      namespace: auth_ns,\n      database: auth_db,\n      username: auth_user,\n      password: auth_pw,\n      scope: auth_scope,\n    })\n    const surreal = new MySurreal(auth_url, {\n      namespace,\n      database,\n      auth,\n    })\n    const db = await surreal.surrealdb()\n    resolve(db)\n  } catch (e) {\n    reject(e)\n  }\n})\n```\n\n#### HTTP ENGINE\n\nWith this configuration, we can use the database's http endpoint. Thus, the `AUTH_SURREAL_URL` should begin with `http` or `https`.\n\n#### Websocket ENGINE\n\nWith this configuration, we can use the database's websocket endpoint. Thus, the `AUTH_SURREAL_URL` should begin with `ws` or `wss`.\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/typeorm.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/typeorm.svg\" height=\"64\" width=\"64\" />\n\n# TypeORM Adapter\n\n## Resources\n\n- [TypeORM documentation](https://typeorm.io)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/typeorm-adapter typeorm\n```\n\n### Environment Variables\n\n```sh\nAUTH_TYPEORM_CONNECTION=postgres://postgres:adminadmin@0.0.0.0:5432/db\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { TypeORMAdapter } from \"@auth/typeorm-adapter\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: TypeORMAdapter(process.env.AUTH_TYPEORM_CONNECTION),\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { TypeORMAdapter } from \"@auth/typeorm-adapter\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: TypeORMAdapter(import.meta.env.AUTH_TYPEORM_CONNECTION),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { TypeORMAdapter } from \"@auth/typeorm-adapter\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: TypeORMAdapter(process.env.AUTH_TYPEORM_CONNECTION),\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { TypeORMAdapter } from \"@auth/typeorm-adapter\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: TypeORMAdapter(process.env.AUTH_TYPEORM_CONNECTION),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n`TypeORMAdapter` takes either a connection string, or a [`ConnectionOptions`](https://github.com/typeorm/typeorm/blob/master/docs/connection-options.md) object as its first parameter.\n\n### Advanced usage\n\n#### Custom models\n\nThe TypeORM adapter uses [`Entity` classes](https://github.com/typeorm/typeorm/blob/master/docs/entities.md) to define the shape of your data.\n\nYou can override the default entities and add additional fields with a custom entities file.\n\n1. Create a file containing your modified entities:\n\n```ts filename=\"lib/entities.ts\" {38-39}\nimport {\n  Entity,\n  PrimaryGeneratedColumn,\n  Column,\n  ManyToOne,\n  OneToMany,\n  ValueTransformer,\n} from \"typeorm\"\n\nconst transformer: Record<\"date\" | \"bigint\", ValueTransformer> = {\n  date: {\n    from: (date: string | null) => date && new Date(parseInt(date, 10)),\n    to: (date?: Date) => date?.valueOf().toString(),\n  },\n  bigint: {\n    from: (bigInt: string | null) => bigInt && parseInt(bigInt, 10),\n    to: (bigInt?: number) => bigInt?.toString(),\n  },\n}\n\n@Entity({ name: \"users\" })\nexport class UserEntity {\n  @PrimaryGeneratedColumn(\"uuid\")\n  id!: string\n\n  @Column({ type: \"varchar\", nullable: true })\n  name!: string | null\n\n  @Column({ type: \"varchar\", nullable: true, unique: true })\n  email!: string | null\n\n  @Column({ type: \"varchar\", nullable: true, transformer: transformer.date })\n  emailVerified!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  image!: string | null\n\n+ @Column({ type: \"varchar\", nullable: true })\n+ role!: string | null\n\n  @OneToMany(() => SessionEntity, (session) => session.userId)\n  sessions!: SessionEntity[]\n\n  @OneToMany(() => AccountEntity, (account) => account.userId)\n  accounts!: AccountEntity[]\n}\n\n@Entity({ name: \"accounts\" })\nexport class AccountEntity {\n  @PrimaryGeneratedColumn(\"uuid\")\n  id!: string\n\n  @Column({ type: \"uuid\" })\n  userId!: string\n\n  @Column()\n  type!: string\n\n  @Column()\n  provider!: string\n\n  @Column()\n  providerAccountId!: string\n\n  @Column({ type: \"varchar\", nullable: true })\n  refresh_token!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  access_token!: string | null\n\n  @Column({\n    nullable: true,\n    type: \"bigint\",\n    transformer: transformer.bigint,\n  })\n  expires_at!: number | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  token_type!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  scope!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  id_token!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  session_state!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  oauth_token_secret!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  oauth_token!: string | null\n\n  @ManyToOne(() => UserEntity, (user) => user.accounts, {\n    createForeignKeyConstraints: true,\n  })\n  user!: UserEntity\n}\n\n@Entity({ name: \"sessions\" })\nexport class SessionEntity {\n  @PrimaryGeneratedColumn(\"uuid\")\n  id!: string\n\n  @Column({ unique: true })\n  sessionToken!: string\n\n  @Column({ type: \"uuid\" })\n  userId!: string\n\n  @Column({ transformer: transformer.date })\n  expires!: string\n\n  @ManyToOne(() => UserEntity, (user) => user.sessions)\n  user!: UserEntity\n}\n\n@Entity({ name: \"verification_tokens\" })\nexport class VerificationTokenEntity {\n  @PrimaryGeneratedColumn(\"uuid\")\n  id!: string\n\n  @Column()\n  token!: string\n\n  @Column()\n  identifier!: string\n\n  @Column({ transformer: transformer.date })\n  expires!: string\n}\n```\n\n2. Pass them to `TypeORMAdapter`\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { TypeORMAdapter } from \"@auth/typeorm-adapter\"\nimport * as entities from \"lib/entities\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: TypeORMAdapter(\"yourconnectionstring\", { entities }),\n})\n```\n\n<Callout type=\"info\">\n  The `synchronize: true` option in TypeORM will generate SQL that exactly\n  matches the entities. This will automatically apply any changes it finds in\n  the entity model. This is a useful option in development.\n</Callout>\n\n<Callout type=\"warning\">\n  `synchronize: true` should not be enabled against production databases as it\n  may cause data loss if the configured schema does not match the expected\n  schema! We recommend that you synchronize/migrate your production database at\n  build-time.\n</Callout>\n\n#### Naming Conventions\n\nIf mixed snake_case and camelCase column names are an issue for you and/or your underlying database system, we recommend using TypeORM's naming strategy feature to change the target field names. There is a package called `typeorm-naming-strategies` which includes a `snake_case` strategy which will translate the fields from how Auth.js expects them, to snake_case in the actual database.\n\nFor example, you can add the naming convention option to the connection object in your NextAuth config.\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { TypeORMAdapter } from \"@auth/typeorm-adapter\"\nimport { SnakeNamingStrategy } from \"typeorm-naming-strategies\"\nimport { ConnectionOptions } from \"typeorm\"\n\nconst connection: ConnectionOptions = {\n  type: \"mysql\",\n  host: \"localhost\",\n  port: 3306,\n  username: \"test\",\n  password: \"test\",\n  database: \"test\",\n  namingStrategy: new SnakeNamingStrategy(),\n}\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: TypeORMAdapter(connection),\n})\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/unstorage.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/unstorage.svg\" width=\"64\" height=\"64\" />\n\n# Unstorage Adapter\n\n## Resources\n\n- [Unstorage documentation](https://unstorage.unjs.io/)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install unstorage @auth/unstorage-adapter\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { UnstorageAdapter } from \"@auth/unstorage-adapter\"\nimport { createStorage } from \"unstorage\"\n\nconst storage = createStorage()\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: UnstorageAdapter(storage),\n  providers: [],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { UnstorageAdapter } from \"@auth/unstorage-adapter\"\nimport { createStorage } from \"unstorage\"\n\nconst storage = createStorage()\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: UnstorageAdapter(storage),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { UnstorageAdapter } from \"@auth/unstorage-adapter\"\nimport { createStorage } from \"unstorage\"\n\nconst storage = createStorage()\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: UnstorageAdapter(storage),\n  providers: [],\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { UnstorageAdapter } from \"@auth/unstorage-adapter\"\nimport { createStorage } from \"unstorage\"\n\nconst storage = createStorage()\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: UnstorageAdapter(storage),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Advanced usage\n\n#### Using multiple apps with a single storage\n\nIf you have multiple Auth.js connected apps using the same storage, you need different key prefixes for every app.\n\nYou can change the prefixes by passing an `options` object as the second argument to the adapter factory function.\n\nThe default values for this object are:\n\n```ts\nconst defaultOptions = {\n  baseKeyPrefix: \"\",\n  accountKeyPrefix: \"user:account:\",\n  accountByUserIdPrefix: \"user:account:by-user-id:\",\n  emailKeyPrefix: \"user:email:\",\n  sessionKeyPrefix: \"user:session:\",\n  sessionByUserIdKeyPrefix: \"user:session:by-user-id:\",\n  userKeyPrefix: \"user:\",\n  verificationTokenKeyPrefix: \"user:token:\",\n}\n```\n\nUsually changing the `baseKeyPrefix` should be enough for this scenario, but for more custom setups, you can also change the prefixes of every single key.\n\n```ts\nimport NextAuth from \"next-auth\"\nimport { UnstorageAdapter } from \"@auth/unstorage-adapter\"\nimport { createStorage } from \"unstorage\"\n\nconst storage = createStorage()\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: UnstorageAdapter(storage, { baseKeyPrefix: \"app2:\" }),\n})\n```\n\n#### Using `getItemRaw`/`setItemRaw` instead of `getItem`/`setItem`\n\nIf you are using storage that supports JSON, you can make it use `getItemRaw/setItemRaw` instead of `getItem/setItem`.\n\nThis is an experimental feature. Please check [unjs/unstorage#142](https://github.com/unjs/unstorage/issues/142) for more information.\n\nYou can enable this functionality by passing `useItemRaw: true` (default: false) in the `options` object as the second argument to the adapter factory function.\n\n```ts\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: UnstorageAdapter(storage, { useItemRaw: true }),\n})\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/upstash-redis.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n<img\n  align=\"right\"\n  src=\"/img/adapters/upstash-redis.svg\"\n  width=\"64\"\n  height=\"64\"\n/>\n\n# Upstash Redis Adapter\n\n## Resources\n\n- [Upstash documentation](https://docs.upstash.com/redis)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @upstash/redis @auth/upstash-redis-adapter\n```\n\n### Environment Variables\n\n```sh\nUPSTASH_REDIS_URL,\nUPSTASH_REDIS_TOKEN\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { UpstashRedisAdapter } from \"@auth/upstash-redis-adapter\"\nimport { Redis } from \"@upstash/redis\"\n\nconst redis = new Redis({\n  url: process.env.UPSTASH_REDIS_URL!,\n  token: process.env.UPSTASH_REDIS_TOKEN!,\n})\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: UpstashRedisAdapter(redis),\n  providers: [],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { UpstashRedisAdapter } from \"@auth/upstash-redis-adapter\"\nimport { Redis } from \"@upstash/redis\"\n\nconst redis = new Redis({\n  url: import.meta.env.UPSTASH_REDIS_URL!,\n  token: import.meta.env.UPSTASH_REDIS_TOKEN!,\n})\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: UpstashRedisAdapter(redis),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { UpstashRedisAdapter } from \"@auth/upstash-redis-adapter\"\nimport { Redis } from \"@upstash/redis\"\n\nconst redis = new Redis({\n  url: process.env.UPSTASH_REDIS_URL!,\n  token: process.env.UPSTASH_REDIS_TOKEN!,\n})\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: UpstashRedisAdapter(redis),\n  providers: [],\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { UpstashRedisAdapter } from \"@auth/upstash-redis-adapter\"\nimport { Redis } from \"@upstash/redis\"\n\nconst redis = new Redis({\n  url: process.env.UPSTASH_REDIS_URL!,\n  token: process.env.UPSTASH_REDIS_TOKEN!,\n})\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: UpstashRedisAdapter(redis),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Advanced usage\n\n#### Using multiple apps with a single Upstash Redis instance\n\nThe Upstash free-tier allows for only one Redis instance. If you have multiple Auth.js connected apps using this instance, you need different key prefixes for every app.\n\nYou can change the prefixes by passing an `options` object as the second argument to the adapter factory function.\n\nThe default values for this object are:\n\n```ts\nconst defaultOptions = {\n  baseKeyPrefix: \"\",\n  accountKeyPrefix: \"user:account:\",\n  accountByUserIdPrefix: \"user:account:by-user-id:\",\n  emailKeyPrefix: \"user:email:\",\n  sessionKeyPrefix: \"user:session:\",\n  sessionByUserIdKeyPrefix: \"user:session:by-user-id:\",\n  userKeyPrefix: \"user:\",\n  verificationTokenKeyPrefix: \"user:token:\",\n}\n```\n\nUsually changing the `baseKeyPrefix` should be enough for this scenario, but for more custom setups, you can also change the prefixes of every single key.\n\n```ts\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: UpstashRedisAdapter(redis, { baseKeyPrefix: \"app2:\" }),\n})\n```\n"
  },
  {
    "path": "docs/pages/getting-started/adapters/xata.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/adapters/xata.svg\" width=\"64\" height=\"64\" />\n\n# Xata Adapter\n\n## Resources\n\n- [Xata documentation](https://xata.io/docs/overview)\n\n## Setup\n\n### Installation\n\n```bash npm2yarn\nnpm install @auth/xata-adapter\n```\n\n```bash\n# Install the Xata CLI globally if you don't already have it\nnpm install --location=global @xata.io/cli\n\n# Login\nxata auth login\n```\n\n### Configuration\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { XataAdapter } from \"@auth/xata-adapter\"\nimport { XataClient } from \"../../../xata\" // Or wherever you've chosen for the generated client\n\nconst client = new XataClient()\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: XataAdapter(client),\n  providers: [],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { XataAdapter } from \"@auth/xata-adapter\"\nimport { XataClient } from \"../../../xata\" // Or wherever you've chosen for the generated client\n\nconst client = new XataClient()\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: XataAdapter(client),\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { XataAdapter } from \"@auth/xata-adapter\"\nimport { XataClient } from \"../../../xata\" // Or wherever you've chosen for the generated client\n\nconst client = new XataClient()\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: XataAdapter(client),\n  providers: [],\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { XataAdapter } from \"@auth/xata-adapter\"\nimport { XataClient } from \"../../../xata\" // Or wherever you've chosen for the generated client\n\nconst client = new XataClient()\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [],\n    adapter: XataAdapter(client),\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n### Xata Setup\n\nThis adapter allows using Auth.js with Xata as a database to store users, sessions, and more. The preferred way to create a Xata project and use Xata databases is using the [Xata Command Line Interface (CLI)](https://docs.xata.io/cli/getting-started).\n\nThe CLI allows generating a `XataClient` that will help you work with Xata in a safe way, and that this adapter depends on.\n\nWhen you're ready, let's create a new Xata project using our Auth.js schema that the Xata adapter can work with. To do that, copy and paste this schema file into your project's directory:\n\n```json filename=\"schema.json\"\n{\n  \"tables\": [\n    {\n      \"name\": \"nextauth_users\",\n      \"columns\": [\n        {\n          \"name\": \"email\",\n          \"type\": \"email\"\n        },\n        {\n          \"name\": \"emailVerified\",\n          \"type\": \"datetime\"\n        },\n        {\n          \"name\": \"name\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"image\",\n          \"type\": \"string\"\n        }\n      ]\n    },\n    {\n      \"name\": \"nextauth_accounts\",\n      \"columns\": [\n        {\n          \"name\": \"user\",\n          \"type\": \"link\",\n          \"link\": {\n            \"table\": \"nextauth_users\"\n          }\n        },\n        {\n          \"name\": \"type\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"provider\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"providerAccountId\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"refresh_token\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"access_token\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"expires_at\",\n          \"type\": \"int\"\n        },\n        {\n          \"name\": \"token_type\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"scope\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"id_token\",\n          \"type\": \"text\"\n        },\n        {\n          \"name\": \"session_state\",\n          \"type\": \"string\"\n        }\n      ]\n    },\n    {\n      \"name\": \"nextauth_verificationTokens\",\n      \"columns\": [\n        {\n          \"name\": \"identifier\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"token\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"expires\",\n          \"type\": \"datetime\"\n        }\n      ]\n    },\n    {\n      \"name\": \"nextauth_users_accounts\",\n      \"columns\": [\n        {\n          \"name\": \"user\",\n          \"type\": \"link\",\n          \"link\": {\n            \"table\": \"nextauth_users\"\n          }\n        },\n        {\n          \"name\": \"account\",\n          \"type\": \"link\",\n          \"link\": {\n            \"table\": \"nextauth_accounts\"\n          }\n        }\n      ]\n    },\n    {\n      \"name\": \"nextauth_users_sessions\",\n      \"columns\": [\n        {\n          \"name\": \"user\",\n          \"type\": \"link\",\n          \"link\": {\n            \"table\": \"nextauth_users\"\n          }\n        },\n        {\n          \"name\": \"session\",\n          \"type\": \"link\",\n          \"link\": {\n            \"table\": \"nextauth_sessions\"\n          }\n        }\n      ]\n    },\n    {\n      \"name\": \"nextauth_sessions\",\n      \"columns\": [\n        {\n          \"name\": \"sessionToken\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"expires\",\n          \"type\": \"datetime\"\n        },\n        {\n          \"name\": \"user\",\n          \"type\": \"link\",\n          \"link\": {\n            \"table\": \"nextauth_users\"\n          }\n        }\n      ]\n    }\n  ]\n}\n```\n\nNow, run the following command:\n\n```bash\nxata init --schema=./path/to/your/schema.json\n```\n\nThe CLI will walk you through a setup process where you choose a [workspace](https://xata.io/docs/api-reference/workspaces) (kind of like a GitHub org or a Vercel team) and an appropriate database. We recommend using a fresh database for this, as we'll augment it with tables that Auth.js needs.\n"
  },
  {
    "path": "docs/pages/getting-started/authentication/_meta.js",
    "content": "export default {\n  oauth: \"OAuth\",\n  email: \"Magic Links\",\n  credentials: \"Credentials\",\n  webauthn: \"WebAuthn 🔬\",\n}\n"
  },
  {
    "path": "docs/pages/getting-started/authentication/credentials.mdx",
    "content": "---\ntitle: Credentials\n---\n\nimport { Steps, Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n# Credentials\n\nTo setup Auth.js with any external authentication mechanisms or use a traditional username/email and password flow, we can use the `Credentials` provider. This provider is designed to forward any credentials inserted into the login form (i.e. username/password, but not limited to) to your authentication service.\n\n<Callout type=\"warning\">\n  The industry has come a long way since usernames and passwords\n  as the go-to mechanism for authenticating and authorizing users to\n  web applications. Therefore, if possible, we recommend a more modern and\n  secure authentication mechanism such as any of the [OAuth\n  providers](/getting-started/authentication/oauth), [Email Magic\n  Links](/getting-started/authentication/email), or [WebAuthn\n  (Passkeys)](/getting-started/authentication/webauthn) options instead.\n\nHowever, we also want to be flexible and support anything\nyou deem appropriate for your application and use case,\nso there are no plans to remove this provider.\n\n</Callout>\n\n<Callout>\n  By default, the Credentials provider does not persist data in the database.\n  However, you can still create and save any data in your database, you just\n  have to provide the necessary logic, eg. to encrypt passwords, add\n  rate-limiting, add password reset functionality, etc.\n</Callout>\n\n<Steps>\n\n### Credentials Provider\n\nTo use the `Credentials Provider`, you’ll first need to import and configure it in your `Auth.js` setup. This provider allows you to implement custom login logic based on form input values.\n\nHere’s how to set it up:\n\n1. Import the provider.\n2. Add it to the `providers` array in your `Auth.js` config.\n3. Define the `credentials` and `authorize` fields.\n\n#### `credentials`\n\nThe `credentials` object defines the input fields displayed on the default sign-in page. These inputs are automatically rendered on the route:\n\n- `/api/auth/signin` (Next.js)\n- `/auth/signin` (Other frameworks)\n\nEach field accepts the following properties:\n\n- `label`: Input label\n- `type`: HTML input type (`text`, `password`, etc.)\n- `placeholder`: Placeholder text\n\n> These fields are also passed to the `authorize` function under the `credentials` argument.\n\nFor more details, see the [Built-in Pages guide](https://authjs.dev/guides/pages/built-in-pages).\n\n```ts\nCredentials({\n  credentials: {\n    email: {\n      type: \"email\",\n      label: \"Email\",\n      placeholder: \"johndoe@gmail.com\",\n    },\n    password: {\n      type: \"password\",\n      label: \"Password\",\n      placeholder: \"*****\",\n    },\n  },\n})\n```\n\n#### `authorize`\n\nThe `authorize` function handles the custom login logic and determines whether the credentials provided are valid.\n\nIt receives the input values defined in `credentials`, and you must return either a user object or `null`. If `null` is returned, the login fails.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\" {2, 8}\nimport NextAuth from \"next-auth\"\nimport Credentials from \"next-auth/providers/credentials\"\n// Your own logic for dealing with plaintext password strings; be careful!\nimport { saltAndHashPassword } from \"@/utils/password\"\n\nexport const { handlers, signIn, signOut, auth } = NextAuth({\n  providers: [\n    Credentials({\n      // You can specify which fields should be submitted, by adding keys to the `credentials` object.\n      // e.g. domain, username, password, 2FA token, etc.\n      credentials: {\n        email: {},\n        password: {},\n      },\n      authorize: async (credentials) => {\n        let user = null\n\n        // logic to salt and hash password\n        const pwHash = saltAndHashPassword(credentials.password)\n\n        // logic to verify if the user exists\n        user = await getUserFromDb(credentials.email, pwHash)\n\n        if (!user) {\n          // No user found, so this is their first attempt to login\n          // Optionally, this is also the place you could do a user registration\n          throw new Error(\"Invalid credentials.\")\n        }\n\n        // return user object with their profile data\n        return user\n      },\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Credentials from \"@auth/qwik/providers/credentials\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Credentials({\n        credentials: {\n          email: { label: \"Email\" },\n          password: { label: \"Password\", type: \"password\" },\n        },\n        async authorize(credentials) {\n          const response = await getUserFromDb(credentials)\n          if (!response.ok) return null\n          return (await response.json()) ?? null\n        },\n      }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\" {2, 8}\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Credentials from \"@auth/sveltekit/providers/credentials\"\n// Your own logic for dealing with plaintext password strings; be careful!\nimport { saltAndHashPassword } from \"@/utils/password\"\n\nexport const { signIn, signOut, handle } = SvelteKitAuth({\n  providers: [\n    Credentials({\n      // You can specify which fields should be submitted, by adding keys to the `credentials` object.\n      // e.g. domain, username, password, 2FA token, etc.\n      credentials: {\n        email: {},\n        password: {},\n      },\n      authorize: async (credentials) => {\n        let user = null\n\n        // logic to salt and hash password\n        const pwHash = saltAndHashPassword(credentials.password)\n\n        // logic to verify if user exists\n        user = await getUserFromDb(credentials.email, pwHash)\n\n        if (!user) {\n          // No user found, so this is their first attempt to login\n          // Optionally, this is also the place you could do a user registration\n          throw new Error(\"Invalid credentials.\")\n        }\n\n        // return JSON object with the user data\n        return user\n      },\n    }),\n  ],\n})\n```\n\nDon't forget to re-export the `handle` in your `./src/hooks.server.ts` file.\n\n```ts filename=\"./src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\" {2, 11}\nimport { ExpressAuth } from \"@auth/express\"\nimport Credentials from \"@auth/express/providers/credentials\"\nimport express from \"express\"\n// Your own logic for dealing with plaintext password strings; be careful!\nimport { saltAndHashPassword } from \"@/utils/password\"\n\nconst app = express()\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [\n      Credentials({\n        // You can specify which fields should be submitted, by adding keys to the `credentials` object.\n        // e.g. domain, username, password, 2FA token, etc.\n        credentials: {\n          email: {},\n          password: {},\n        },\n        authorize: async (credentials) => {\n          let user = null\n\n          // logic to salt and hash password\n          const pwHash = saltAndHashPassword(credentials.password)\n\n          // logic to verify if the user exists\n          user = await getUserFromDb(credentials.email, pwHash)\n\n          if (!user) {\n            // No user found, so this is their first attempt to login\n            // Optionally, this is also the place you could do a user registration\n            throw new Error(\"Invalid credentials.\")\n          }\n\n          // return user object with the their profile data\n          return user\n        },\n      }),\n    ],\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\n<Callout type=\"info\">\n  If you're using TypeScript, you can [augment the `User`\n  interface](/getting-started/typescript#module-augmentation) to match the\n  response of your `authorize` callback, so whenever you read the user in other\n  callbacks (like the `jwt`) the type will match correctly.\n</Callout>\n\n### Signin Form\n\nFinally, let's create a simple sign-in form.\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"./components/sign-in.tsx\" {13, 17} /formData/\nimport { signIn } from \"@/auth\"\n\nexport function SignIn() {\n  return (\n    <form\n      action={async (formData) => {\n        \"use server\"\n        await signIn(\"credentials\", formData)\n      }}\n    >\n      <label>\n        Email\n        <input name=\"email\" type=\"email\" />\n      </label>\n      <label>\n        Password\n        <input name=\"password\" type=\"password\" />\n      </label>\n      <button>Sign In</button>\n    </form>\n  )\n}\n```\n\n</Code.Next>\n<Code.NextClient>\n\n```tsx filename=\"./components/sign-in.tsx\"\n\"use client\"\nimport { signIn } from \"next-auth/react\"\n\nexport function SignIn() {\n  const credentialsAction = (formData: FormData) => {\n    signIn(\"credentials\", formData)\n  }\n\n  return (\n    <form action={credentialsAction}>\n      <label htmlFor=\"credentials-email\">\n        Email\n        <input type=\"email\" id=\"credentials-email\" name=\"email\" />\n      </label>\n      <label htmlFor=\"credentials-password\">\n        Password\n        <input type=\"password\" id=\"credentials-password\" name=\"password\" />\n      </label>\n      <input type=\"submit\" value=\"Sign In\" />\n    </form>\n  )\n}\n```\n\n</Code.NextClient>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/index.tsx\"\nimport { component$ } from \"@builder.io/qwik\"\nimport { Form } from \"@builder.io/qwik-city\"\nimport { useSignIn } from \"./plugin@auth\"\n\nexport default component$(() => {\n  const signInSig = useSignIn()\n\n  return (\n    <Form action={signInSig}>\n      <label>\n        Email\n        <input name=\"email\" type=\"email\" />\n      </label>\n      <label>\n        Password\n        <input name=\"password\" type=\"password\" />\n      </label>\n      <button>Sign In</button>\n    </Form>\n  )\n})\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```svelte filename=\"src/route/+page.svelte\" /bind:value/ /signIn/\n<script>\n  import { signIn } from \"../auth\"\n  import { page } from \"$app/stores\"\n\n  let email = \"\"\n  let password = \"\"\n</script>\n\n<div>\n  <form>\n    <label>\n      Email\n      <input name=\"email\" type=\"email\" bind:value={email} />\n    </label>\n    <label>\n      Password\n      <input name=\"password\" type=\"password\" bind:value={password} />\n    </label>\n    <button on:click={() => signIn(\"credentials\", { email, password })}>\n      Log in\n    </button>\n  </form>\n</div>\n```\n\n</Code.Svelte>\n\n<Code.Express>\n\n```html filename=\"views/signin.html\"\n<!doctype html>\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>Sign In</title>\n  </head>\n  <body>\n    <h1>Sign In</h1>\n    <form action=\"/auth/signin\" method=\"POST\">\n      <label for=\"email\">Email:</label>\n      <input type=\"email\" name=\"email\" id=\"email\" required />\n      <br />\n      <label for=\"password\">Password:</label>\n      <input type=\"password\" name=\"password\" id=\"password\" required />\n      <br />\n      <button type=\"submit\">Sign In</button>\n    </form>\n  </body>\n</html>\n```\n\n</Code.Express>\n\n</Code>\n\n</Steps>\n\n## Validating credentials\n\nAlways validate the credentials server-side, i.e. by leveraging a schema validation library like [Zod](https://zod.dev).\n\n```bash npm2yarn\nnpm install zod\n```\n\nNext, we'll set up the schema and parsing in our `auth.ts` configuration file, using the `authorize` callback on the `Credentials` provider.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./lib/zod.ts\"\nimport { object, string } from \"zod\"\n\nexport const signInSchema = object({\n  email: string({ required_error: \"Email is required\" })\n    .min(1, \"Email is required\")\n    .email(\"Invalid email\"),\n  password: string({ required_error: \"Password is required\" })\n    .min(1, \"Password is required\")\n    .min(8, \"Password must be more than 8 characters\")\n    .max(32, \"Password must be less than 32 characters\"),\n})\n```\n\n```ts filename=\"./auth.ts\" {22}\nimport NextAuth from \"next-auth\"\nimport { ZodError } from \"zod\"\nimport Credentials from \"next-auth/providers/credentials\"\nimport { signInSchema } from \"./lib/zod\"\n// Your own logic for dealing with plaintext password strings; be careful!\nimport { saltAndHashPassword } from \"@/utils/password\"\nimport { getUserFromDb } from \"@/utils/db\"\n\nexport const { handlers, auth } = NextAuth({\n  providers: [\n    Credentials({\n      // You can specify which fields should be submitted, by adding keys to the `credentials` object.\n      // e.g. domain, username, password, 2FA token, etc.\n      credentials: {\n        email: {},\n        password: {},\n      },\n      authorize: async (credentials) => {\n        try {\n          let user = null\n\n          const { email, password } = await signInSchema.parseAsync(credentials)\n\n          // logic to salt and hash password\n          const pwHash = saltAndHashPassword(password)\n\n          // logic to verify if the user exists\n          user = await getUserFromDb(email, pwHash)\n\n          if (!user) {\n            throw new Error(\"Invalid credentials.\")\n          }\n\n          // return JSON object with the user data\n          return user\n        } catch (error) {\n          if (error instanceof ZodError) {\n            // Return `null` to indicate that the credentials are invalid\n            return null\n          }\n        }\n      },\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"./lib/zod.ts\"\nimport { object, string } from \"zod\"\n\nexport const signInSchema = object({\n  email: string({ required_error: \"Email is required\" })\n    .min(1, \"Email is required\")\n    .email(\"Invalid email\"),\n  password: string({ required_error: \"Password is required\" })\n    .min(1, \"Password is required\")\n    .min(8, \"Password must be more than 8 characters\")\n    .max(32, \"Password must be less than 32 characters\"),\n})\n```\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Credentials from \"@auth/qwik/providers/credentials\"\nimport { signInSchema } from \"./lib/zod\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Credentials({\n        credentials: {\n          email: { label: \"Email\" },\n          password: { label: \"Password\", type: \"password\" },\n        },\n        async authorize(credentials) {\n          const { email, password } = await signInSchema.parseAsync(credentials)\n\n          // Your logic here\n        },\n      }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./lib/zod.ts\"\nimport { object, string } from \"zod\"\n\nexport const signInSchema = object({\n  email: string({ required_error: \"Email is required\" })\n    .min(1, \"Email is required\")\n    .email(\"Invalid email\"),\n  password: string({ required_error: \"Password is required\" })\n    .min(1, \"Password is required\")\n    .min(8, \"Password must be more than 8 characters\")\n    .max(32, \"Password must be less than 32 characters\"),\n})\n```\n\n```ts filename=\"./auth.ts\" {22-23}\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport { ZodError } from \"zod\"\nimport Credentials from \"@auth/sveltekit/providers/credentials\"\nimport { signInSchema } from \"./lib/zod\"\n// Your own logic for dealing with plaintext password strings; be careful!\nimport { saltAndHashPassword } from \"@/utils/password\"\nimport { getUserFromDb } from \"@/utils/db\"\n\nexport const { handle } = SvelteKitAuth({\n  providers: [\n    Credentials({\n      // You can specify which fields should be submitted, by adding keys to the `credentials` object.\n      // e.g. domain, username, password, 2FA token, etc.\n      credentials: {\n        email: {},\n        password: {},\n      },\n      authorize: async (credentials) => {\n        try {\n          let user = null\n\n          const { email, password } =\n            await createUserSchema.parseAsync(credentials)\n\n          // logic to salt and hash password\n          const pwHash = saltAndHashPassword(password)\n\n          // logic to verify if the user exists\n          user = await getUserFromDb(email, pwHash)\n\n          if (!user) {\n            throw new Error(\"Invalid credentials.\")\n          }\n\n          // return JSON object with the user data\n          return user\n        } catch (error) {\n          if (error instanceof ZodError) {\n            // Return `null` to indicate that the credentials are invalid\n            return null\n          }\n        }\n      },\n    }),\n  ],\n})\n```\n\n</Code.Svelte>\n</Code>\n\n<Callout>\n\nWhen authorize returns null, Auth.js handles the error in one of two ways:\n\nUsing built-in pages:\n\n- The user is redirected to the login page with the query string: `?error=CredentialsSignin&code=credentials`. You can customize the code using the [credentials provider options](https://authjs.dev/reference/core/providers/credentials#returns).\n- Using form actions or custom error handling (e.g., in Remix, SvelteKit): The error is thrown as credentialssignin and must be caught manually in your server action. See more in the [Auth.js error reference](https://authjs.dev/reference/core/errors#credentialssignin).\n\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/authentication/email.mdx",
    "content": "---\ntitle: Email Providers\n---\n\nimport { useState } from \"react\"\nimport { RichTabs } from \"@/components/RichTabs\"\n\nimport { Callout, Steps, Tabs } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\nimport { Link } from \"@/components/Link\"\nimport { Screenshot } from \"@/components/Screenshot\"\nimport MagicLink from \"../../../public/img/magic-links/start.webp\"\n\n# Email Provider\n\nThis login mechanism starts by the user providing their email address at the login form. Then a **Verification Token** is sent to the provided email address. The user then has 24 hours to click the link in the email body to \"consume\" that token and register their account, otherwise the verification token will expire and they will have to request a new one.\n\n<Callout>\n  An Email Provider can be used with both [JSON Web Tokens](https://jwt.io/) and\n  [database](/concepts/session-strategies#database-session) session, whichever\n  you choose, you **must still configure a database** so that Auth.js can save\n  the verification tokens and look them up when the user attempts to login. It\n  is not possible to enable an email provider without using a database.\n</Callout>\n\n### Providers\n\n<div className=\"flex flex-col w-full gap-6 items-start\">\n  <RichTabs\n    orientation=\"horizontal\"\n    tabKey=\"Resend\"\n    defaultValue=\"resend\"\n    className=\"w-full flex flex-col\"\n  >\n    <RichTabs.List className=\"grid grid-cols-[repeat(auto-fit,140px)] justify-center gap-4\">\n      <RichTabs.Trigger\n        value=\"forwardemail\"\n        orientation=\"vertical\"\n        className=\"!border border-neutral-200 dark:border-neutral-700 aria-selected:!bg-neutral-100 aria-selected:dark:!bg-neutral-950 dark:!bg-neutral-900 !bg-white !h-auto !w-auto !gap-2 !justify-start p-6 px-8 rounded-md outline-none transition-all duration-300 hover:bg-neutral-200 !font-normal\"\n      >\n        <img className=\"size-16 dark:bg-[currentColor]\" src={`/img/providers/forwardemail.svg`} />\n        <div className=\"text-sm text-center line-clamp-1\">Forward Email</div>\n      </RichTabs.Trigger>\n      <RichTabs.Trigger\n        value=\"resend\"\n        orientation=\"vertical\"\n        className=\"!border border-neutral-200 dark:border-neutral-700 aria-selected:!bg-neutral-100 aria-selected:dark:!bg-neutral-950 dark:!bg-neutral-900 !bg-white !h-auto !w-auto !gap-2 !justify-start p-6 px-8 rounded-md outline-none transition-all duration-300 hover:bg-neutral-200 !font-normal\"\n      >\n        <img className=\"size-16 dark:bg-[currentColor]\" src={`/img/providers/resend.svg`} />\n        <div className=\"text-sm text-center\">Resend</div>\n      </RichTabs.Trigger>\n      <RichTabs.Trigger\n        value=\"sendgrid\"\n        orientation=\"vertical\"\n        className=\"!border border-neutral-200 dark:border-neutral-700 aria-selected:!bg-neutral-100 aria-selected:dark:!bg-neutral-950 dark:!bg-neutral-900 !bg-white !h-auto !w-auto !gap-2 !justify-start p-6 px-8 rounded-md outline-none transition-all duration-300 hover:bg-neutral-200 !font-normal\"\n      >\n        <img className=\"size-16\" src={`/img/providers/sendgrid.svg`} />\n        <div className=\"text-sm text-center\">Sendgrid</div>\n      </RichTabs.Trigger>\n      <RichTabs.Trigger\n        value=\"nodemailer\"\n        orientation=\"vertical\"\n        className=\"!border border-neutral-200 dark:border-neutral-700 aria-selected:!bg-neutral-100 aria-selected:dark:!bg-neutral-950 dark:!bg-neutral-900 !bg-white !h-auto !w-auto !gap-2 !justify-start p-6 px-8 rounded-md outline-none transition-all duration-300 hover:bg-neutral-200 !font-normal\"\n      >\n        <img className=\"size-16\" src={`/img/providers/nodemailer.svg`} />\n        <div className=\"text-sm text-center\">Nodemailer</div>\n      </RichTabs.Trigger>\n      <RichTabs.Trigger\n        value=\"postmark\"\n        orientation=\"vertical\"\n        className=\"!border border-neutral-200 dark:border-neutral-700 aria-selected:!bg-neutral-100 aria-selected:dark:!bg-neutral-950 dark:!bg-neutral-900 !bg-white !h-auto !w-auto !gap-2 !justify-start p-6 px-8 rounded-md outline-none transition-all duration-300 hover:bg-neutral-200 !font-normal\"\n      >\n        <img className=\"size-16\" src={`/img/providers/postmark.svg`} />\n        <div className=\"text-sm text-center\">Postmark</div>\n      </RichTabs.Trigger>\n            <RichTabs.Trigger\n        value=\"loops\"\n        orientation=\"vertical\"\n        className=\"!border border-neutral-200 dark:border-neutral-700 aria-selected:!bg-neutral-100 aria-selected:dark:!bg-neutral-950 dark:!bg-neutral-900 !bg-white !h-auto !w-auto !gap-2 !justify-start p-6 px-8 rounded-md outline-none transition-all duration-300 hover:bg-neutral-200 !font-normal\"\n      >\n        <img className=\"size-16\" src={`/img/providers/loops.svg`} />\n        <div className=\"text-sm text-center\">Loops</div>\n      </RichTabs.Trigger>\n      <RichTabs.Trigger\n        value=\"mailgun\"\n        orientation=\"vertical\"\n        className=\"!border border-neutral-200 dark:border-neutral-700 aria-selected:!bg-neutral-100 aria-selected:dark:!bg-neutral-950 dark:!bg-neutral-900 !bg-white !h-auto !w-auto !gap-2 !justify-start p-6 px-8 rounded-md outline-none transition-all duration-300 hover:bg-neutral-200 !font-normal\"\n      >\n        <img className=\"size-16\" src={`/img/providers/mailgun.svg`} />\n        <div className=\"text-sm text-center\">Mailgun</div>\n      </RichTabs.Trigger>\n    </RichTabs.List>\n  <RichTabs.Content\n    orientation=\"vertical\"\n    value=\"forwardemail\"\n    className=\"h-full !border-0 !w-full\"\n    tabIndex={-1}\n  >\n\n### Forward Email Setup\n\n<Steps>\n\n### Database Adapter\n\nPlease make sure you've [setup a database adapter](/getting-started/database), as mentioned earlier,\na database is required for passwordless login to work as verification tokens need to be stored.\n\n### Setup Environment Variables\n\nAuth.js will automatically pick up these if formatted like the example above.\nYou can [also use a different name for the environment variables](/guides/environment-variables#oauth-variables) if needed, but then you’ll need to pass them to the provider manually.\n\n```bash filename=\".env\"\nAUTH_FORWARDEMAIL_KEY=abc123\n```\n\n### Setup Provider\n\nLet’s enable `ForwardEmail` as a sign in option in our Auth.js configuration. You’ll have to import the `ForwardEmail` provider from the package and pass it to the providers array we setup earlier in the Auth.js config file:\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport ForwardEmail from \"next-auth/providers/forwardemail\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [ForwardEmail],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport ForwardEmail from \"@auth/qwik/providers/forwardemail\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [ForwardEmail],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport ForwardEmail from \"@auth/sveltekit/providers/forwardemail\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [ForwardEmail],\n})\n```\n\n```ts filename=\"./src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n\n### Add Signin Button\n\nNext, we can add a signin button somewhere in your application like the Navbar. This will send an email to the user containing the magic link to sign in.\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"./components/sign-in.tsx\"\nimport { signIn } from \"../../auth.ts\"\n\nexport function SignIn() {\n  return (\n    <form\n      action={async (formData) => {\n        \"use server\"\n        await signIn(\"forwardemail\", formData)\n      }}\n    >\n      <input type=\"text\" name=\"email\" placeholder=\"Email\" />\n      <button type=\"submit\">Signin with Forward Email</button>\n    </form>\n  )\n}\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"./components/sign-in.tsx\"\nimport { component$ } from \"@builder.io/qwik\"\nimport { useSignIn } from \"./plugin@auth\"\n\nexport default component$(() => {\n  const signInSig = useSignIn()\n\n  return (\n    <button\n      onClick$={() => signInSig.submit({ redirectTo: \"/\" })}\n    >\n      SignIn\n    </button>\n  )\n})\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```html filename=\"src/routes/+page.svelte\"\n<script lang=\"ts\">\n  import { SignIn } from \"@auth/sveltekit/components\"\n</script>\n\n<div>\n  <nav>\n    <img src=\"/img/logo.svg\" alt=\"Company Logo\" />\n    <SignIn provider=\"forwardemail\" />\n  </nav>\n</div>\n```\n\n</Code.Svelte>\n</Code>\n\n### Signin\n\nStart your application, once the user enters their Email and clicks on the signin button, they'll be redirected to a page that asks them to check their email. When they click on the link in their email, they will be signed in.\n\n</Steps>\n\n<Callout type=\"info\">\n  Check our [Customising magic links\n  emails](/getting-started/providers/forwardemail#customization) to learn how to\n  change the look and feel of the emails the user receives to sign in.\n</Callout>\n\nFor more information on this provider go to the [Forward Email docs page](/getting-started/providers/forwardemail).\n\n</RichTabs.Content>\n\n<RichTabs.Content\n  orientation=\"vertical\"\n  value=\"resend\"\n  className=\"h-full !border-0 !w-full\"\n  tabIndex={-1}\n>\n\n### Resend Setup\n\n<Steps>\n\n### Database Adapter\n\nPlease make sure you've [setup a database adapter](/getting-started/database), as mentioned earlier,\na database is required for passwordless login to work as verification tokens need to be stored.\n\n### Setup Environment Variables\n\nAuth.js will automatically pick up these if formatted like the example above.\nYou can [also use a different name for the environment variables](/guides/environment-variables#oauth-variables) if needed, but then you’ll need to pass them to the provider manually.\n\n```bash filename=\".env\"\nAUTH_RESEND_KEY=abc123\n```\n\n### Setup Provider\n\nLet’s enable `Resend` as a sign in option in our Auth.js configuration. You’ll have to import the `Resend` provider from the package and pass it to the providers array we setup earlier in the Auth.js config file:\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Resend from \"next-auth/providers/resend\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Resend],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Resend from \"@auth/qwik/providers/resend\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Resend],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport Resend from \"@auth/sveltekit/providers/resend\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Resend],\n})\n```\n\n```ts filename=\"./src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n\n### Add Signin Button\n\nNext, we can add a signin button somewhere in your application like the Navbar. This will send an email to the user containing the magic link to sign in.\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"./components/sign-in.tsx\"\nimport { signIn } from \"../../auth.ts\"\n\nexport function SignIn() {\n  return (\n    <form\n      action={async (formData) => {\n        \"use server\"\n        await signIn(\"resend\", formData)\n      }}\n    >\n      <input type=\"text\" name=\"email\" placeholder=\"Email\" />\n      <button type=\"submit\">Signin with Resend</button>\n    </form>\n  )\n}\n```\n\n</Code.Next>\n<Code.NextClient>\n\n```tsx filename=\"./components/sign-in.tsx\"\n\"use client\"\nimport { signIn } from \"next-auth/react\"\n\nexport function SignIn() {\n  const resendAction = (formData: FormData) => {\n    signIn(\"resend\", formData)\n  }\n\n  return (\n    <form action={resendAction}>\n      <label htmlFor=\"email-resend\">\n        Email\n        <input type=\"email\" id=\"email-resend\" name=\"email\" />\n      </label>\n      <input type=\"submit\" value=\"Signin with Resend\" />\n    </form>\n  )\n}\n```\n\n</Code.NextClient>\n<Code.Qwik>\n\n```ts filename=\"./components/sign-in.tsx\"\nimport { component$ } from \"@builder.io/qwik\"\nimport { useSignIn } from \"./plugin@auth\"\n\nexport default component$(() => {\n  const signInSig = useSignIn()\n\n  return (\n    <button\n      onClick$={() => signInSig.submit({ redirectTo: \"/\" })}\n    >\n      SignIn\n    </button>\n  )\n})\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```html filename=\"src/routes/+page.svelte\"\n<script lang=\"ts\">\n  import { SignIn } from \"@auth/sveltekit/components\"\n</script>\n\n<div>\n  <nav>\n    <img src=\"/img/logo.svg\" alt=\"Company Logo\" />\n    <SignIn provider=\"resend\" />\n  </nav>\n</div>\n```\n\n</Code.Svelte>\n</Code>\n\n### Signin\n\nStart your application, once the user enters their Email and clicks on the signin button, they'll be redirected to a page that asks them to check their email. When they click on the link in their email, they will be signed in.\n\n</Steps>\n\n<Callout type=\"info\">\n  Check our [Customising magic links\n  emails](/getting-started/providers/resend#customization) to learn how to\n  change the look and feel of the emails the user receives to sign in.\n</Callout>\n\nFor more information on this provider go to the [Resend docs page](/getting-started/providers/resend).\n\n</RichTabs.Content>\n\n<RichTabs.Content\n  orientation=\"vertical\"\n  value=\"sendgrid\"\n  className=\"h-full !border-0 !w-full\"\n  tabIndex={-1}\n>\n\n### Sendgrid Setup\n\n<Steps>\n\n### Database Adapter\n\nPlease make sure you've [setup a database adapter](/getting-started/database), as mentioned earlier,\na database is required for passwordless login to work as verification tokens need to be stored.\n\n### Setup Environment Variables\n\nAuth.js will automatically pick up these if formatted like the example above.\nYou can [also use a different name for the environment variables](/guides/environment-variables#oauth-variables) if needed, but then you’ll need to pass them to the provider manually.\n\n```bash filename=\".env\"\nAUTH_SENDGRID_KEY=abc123\n```\n\n### Setup Provider\n\nLet’s enable `Sendgrid` as a sign in option in our Auth.js configuration. You’ll have to import the `Sendgrid` provider from the package and pass it to the providers array we setup earlier in the Auth.js config file:\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Sendgrid from \"next-auth/providers/sendgrid\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Sendgrid],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Sendgrid from \"@auth/qwik/providers/sendgrid\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Sendgrid],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport Sendgrid from \"@auth/sveltekit/providers/sendgrid\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Sendgrid],\n})\n```\n\n```ts filename=\"./src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n\n### Add Signin Button\n\nNext, we can add a signin button somewhere in your application like the Navbar. This will send an email to the user containing the magic link to sign in.\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"./components/sign-in.tsx\"\nimport { signIn } from \"../../auth.ts\"\n\nexport function SignIn() {\n  return (\n    <form\n      action={async (formData) => {\n        \"use server\"\n        await signIn(\"sendgrid\", formData)\n      }}\n    >\n      <input type=\"text\" name=\"email\" placeholder=\"Email\" />\n      <button type=\"submit\">Signin with Sendgrid</button>\n    </form>\n  )\n}\n```\n\n</Code.Next>\n<Code.NextClient>\n\n```tsx filename=\"./components/sign-in.tsx\"\n\"use client\"\nimport { signIn } from \"next-auth/react\"\n\nexport function SignIn() {\n  const sendgridAction = (formData: FormData) => {\n    signIn(\"sendgrid\", formData)\n  }\n\n  return (\n    <form action={sendgridAction}>\n      <label htmlFor=\"email-sendgrid\">\n        Email\n        <input type=\"email\" id=\"email-sendgrid\" name=\"email\" />\n      </label>\n      <input type=\"submit\" value=\"Signin with Sendgrid\" />\n    </form>\n  )\n}\n```\n\n</Code.NextClient>\n<Code.Qwik>\n\n```ts filename=\"./components/sign-in.tsx\"\nimport { component$ } from \"@builder.io/qwik\"\nimport { useSignIn } from \"./plugin@auth\"\n\nexport default component$(() => {\n  const signInSig = useSignIn()\n\n  return (\n    <button\n      onClick$={() => signInSig.submit({ redirectTo: \"/\" })}\n    >\n      SignIn\n    </button>\n  )\n})\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```html filename=\"src/routes/+page.svelte\"\n<script lang=\"ts\">\n  import { SignIn } from \"@auth/sveltekit/components\"\n</script>\n\n<div>\n  <nav>\n    <img src=\"/img/logo.svg\" alt=\"Company Logo\" />\n    <SignIn provider=\"sendgrid\" />\n  </nav>\n</div>\n```\n\n</Code.Svelte>\n</Code>\n\n### Signin\n\nStart your application, once the user enters their Email and clicks on the signin button, they'll be redirected to a page that asks them to check their email. When they click on the link in their email, they will be signed in.\n\n</Steps>\n\n<Callout type=\"info\">\n  Check our [Customising magic links\n  emails](/getting-started/providers/sendgrid#customization) to learn how to\n  change the look and feel of the emails the user receives to sign in.\n</Callout>\n\nFor more information on this provider go to the [Sendgrid docs page](/getting-started/providers/sendgrid).\n\n</RichTabs.Content>\n\n<RichTabs.Content\n  orientation=\"vertical\"\n  value=\"nodemailer\"\n  className=\"h-full !border-0 !w-full\"\n  tabIndex={-1}\n>\n\n### Nodemailer Setup\n\n<Steps>\n\n### Install `nodemailer`\n\nAuth.js does not include `nodemailer` as a dependency, so you'll need to install it yourself if you want to use the Nodemailer provider.\n\n```sh npm2yarn\nnpm install nodemailer\n```\n\nYou will need access to an SMTP server, ideally from one of [the services known to work with nodemailer](https://community.nodemailer.com/2-0-0-beta/setup-smtp/well-known-services/). Alternatively, Nodemailer does support [other transport mechanisms](https://nodemailer.com/transports/).\n\n### Database Adapter\n\nPlease make sure you've [setup a database adapter](/getting-started/database), as mentioned earlier,\na database is required for magic link login to work as verification tokens need to be stored.\n\n### SMTP Transport Configuration\n\nThere are two ways to configure the SMTP server connection: using a connection string or a configuration object.\n\n<Tabs items={['Connection string', 'Configuration object']}>\n  <Tabs.Tab>\n\n```bash filename=\".env\"\nEMAIL_SERVER=smtp://username:password@smtp.example.com:587\nEMAIL_FROM=noreply@example.com\n```\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Nodemailer from \"next-auth/providers/nodemailer\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Nodemailer({\n      server: process.env.EMAIL_SERVER,\n      from: process.env.EMAIL_FROM,\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Nodemailer from \"@auth/qwik/providers/nodemailer\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Nodemailer({\n        server: import.meta.env.EMAIL_SERVER,\n        from: import.meta.env.EMAIL_FROM,\n      }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport Nodemailer from \"@auth/sveltekit/providers/nodemailer\"\nimport { env } from \"$env/dynamic/private\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    Nodemailer({\n      server: env.EMAIL_SERVER,\n      from: env.EMAIL_FROM,\n    }),\n  ],\n})\n```\n\n```ts filename=\"./src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n\n  </Tabs.Tab>\n  <Tabs.Tab>\n\n```bash filename=\".env\"\nEMAIL_SERVER_USER=username\nEMAIL_SERVER_PASSWORD=password\nEMAIL_SERVER_HOST=smtp.example.com\nEMAIL_SERVER_PORT=587\nEMAIL_FROM=noreply@example.com\n```\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Nodemailer from \"next-auth/providers/nodemailer\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Nodemailer({\n      server: {\n        host: process.env.EMAIL_SERVER_HOST,\n        port: process.env.EMAIL_SERVER_PORT,\n        auth: {\n          user: process.env.EMAIL_SERVER_USER,\n          pass: process.env.EMAIL_SERVER_PASSWORD,\n        },\n      },\n      from: process.env.EMAIL_FROM,\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Nodemailer from \"@auth/qwik/providers/nodemailer\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Nodemailer({\n        server: {\n          host: import.meta.env.EMAIL_SERVER_HOST,\n          port: import.meta.env.EMAIL_SERVER_PORT,\n          auth: {\n            user: import.meta.env.EMAIL_SERVER_USER,\n            pass: import.meta.env.EMAIL_SERVER_PASSWORD,\n          },\n        },\n        from: import.meta.env.EMAIL_FROM,\n      }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport Nodemailer from \"@auth/sveltekit/providers/nodemailer\"\nimport { env } from \"$env/dynamic/private\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    Nodemailer({\n      server: {\n        host: env.EMAIL_SERVER_HOST,\n        port: env.EMAIL_SERVER_PORT,\n        auth: {\n          user: env.EMAIL_SERVER_USER,\n          pass: env.EMAIL_SERVER_PASSWORD,\n        },\n      },\n      from: env.EMAIL_FROM,\n    }),\n  ],\n})\n```\n\n```ts filename=\"src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n\n  </Tabs.Tab>\n</Tabs>\n\n### Signin Button\n\nNext, we can add a sign in button somewhere in your application like the Navbar. This will forward the user on to the Auth.js default signin page.\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"./components/sign-in.tsx\"\nimport { signIn } from \"../../auth.ts\"\n\nexport function SignIn() {\n  return (\n    <form\n      action={async () => {\n        \"use server\"\n        await signIn()\n      }}\n    >\n      <button type=\"submit\">Sign In</button>\n    </form>\n  )\n}\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"./components/sign-in.tsx\"\nimport { component$ } from \"@builder.io/qwik\"\nimport { useSignIn } from \"./plugin@auth\"\n\nexport default component$(() => {\n  const signInSig = useSignIn()\n\n  return (\n    <button\n      onClick$={() => signInSig.submit({ redirectTo: \"/\" })}\n    >\n      SignIn\n    </button>\n  )\n})\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"src/routes/+page.svelte\"\n<script lang=\"ts\">\n  import { signIn } from '@auth/sveltekit/client'\n</script>\n\n<div>\n  <nav>\n    <img src=\"/img/logo.svg\" alt=\"Company Logo\" />\n    <button on:click={signIn}>Signin</button>\n  </nav>\n</div>\n```\n\n</Code.Svelte>\n</Code>\n\n### Signin\n\nStart your application, click on the sign in button we just added, and you should see Auth.js built-in sign in page with the option to sign in with your email:\n\n<Screenshot src={MagicLink} alt=\"Screenshot of sign in page\" />\n\nInsert your email and click \"Sign in with Email\". You should receive an email from Auth.js, click on it and should be redirected to your application, landing already authenticated.\n\n</Steps>\n\nFor more information on this provider go to the [Nodemailer docs page](/getting-started/providers/nodemailer).\n\n</RichTabs.Content>\n<RichTabs.Content\n  orientation=\"vertical\"\n  value=\"postmark\"\n  className=\"h-full !border-0 !w-full\"\n  tabIndex={-1}\n>\n\n### Postmark Setup\n\n<Steps>\n\n### Database Adapter\n\nPlease make sure you've [setup a database adapter](/getting-started/database), as mentioned earlier,\na database is required for passwordless login to work as verification tokens need to be stored.\n\n### Setup Environment Variables\n\nAuth.js will automatically pick up these if formatted like the example above.\nYou can [also use a different name for the environment variables](/guides/environment-variables#oauth-variables) if needed, but then you’ll need to pass them to the provider manually.\n\n```bash filename=\".env\"\nAUTH_POSTMARK_KEY=abc123\n```\n\n### Setup Provider\n\nLet’s enable `Postmark` as a sign in option in our Auth.js configuration. You’ll have to import the `Postmark` provider from the package and pass it to the providers array we setup earlier in the Auth.js config file:\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Postmark from \"next-auth/providers/postmark\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Postmark],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Postmark from \"@auth/qwik/providers/postmark\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Postmark],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport Postmark from \"@auth/sveltekit/providers/postmark\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Postmark],\n})\n```\n\n```ts filename=\"./src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n\n### Add Signin Button\n\nNext, we can add a signin button somewhere in your application like the Navbar. This will send an email to the user containing the magic link to sign in.\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"./components/sign-in.tsx\"\nimport { signIn } from \"../../auth.ts\"\n\nexport function SignIn() {\n  return (\n    <form\n      action={async (formData) => {\n        \"use server\"\n        await signIn(\"postmark\", formData)\n      }}\n    >\n      <input type=\"text\" name=\"email\" placeholder=\"Email\" />\n      <button type=\"submit\">Signin with Postmark</button>\n    </form>\n  )\n}\n```\n\n</Code.Next>\n<Code.NextClient>\n\n```tsx filename=\"./components/sign-in.tsx\"\n\"use client\"\nimport { signIn } from \"next-auth/react\"\n\nexport function SignIn() {\n  const postmarkAction = (formData: FormData) => {\n    signIn(\"postmark\", formData)\n  }\n\n  return (\n    <form action={postmarkAction}>\n      <label htmlFor=\"email-postmark\">\n        Email\n        <input type=\"email\" id=\"email-postmark\" name=\"email\" />\n      </label>\n      <input type=\"submit\" value=\"Signin with Postmark\" />\n    </form>\n  )\n}\n```\n\n</Code.NextClient>\n<Code.Qwik>\n\n```ts filename=\"./components/sign-in.tsx\"\nimport { component$ } from \"@builder.io/qwik\"\nimport { useSignIn } from \"./plugin@auth\"\n\nexport default component$(() => {\n  const signInSig = useSignIn()\n\n  return (\n    <button\n      onClick$={() => signInSig.submit({ redirectTo: \"/\" })}\n    >\n      SignIn\n    </button>\n  )\n})\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```html filename=\"src/routes/+page.svelte\"\n<script lang=\"ts\">\n  import { SignIn } from \"@auth/sveltekit/components\"\n</script>\n\n<div>\n  <nav>\n    <img src=\"/img/logo.svg\" alt=\"Company Logo\" />\n    <SignIn provider=\"postmark\" />\n  </nav>\n</div>\n```\n\n</Code.Svelte>\n</Code>\n\n### Signin\n\nStart your application, once the user enters their Email and clicks on the signin button, they'll be redirected to a page that asks them to check their email. When they click on the link in their email, they will be signed in.\n\n</Steps>\n\n<Callout type=\"info\">\n  Check our [Customising magic links\n  emails](/getting-started/providers/postmark#customization) to learn how to\n  change the look and feel of the emails the user receives to sign in.\n</Callout>\n\nFor more information on this provider go to the [Postmark docs page](/getting-started/providers/postmark).\n\n</RichTabs.Content>\n<RichTabs.Content\norientation=\"vertical\"\nvalue=\"loops\"\nclassName=\"h-full !border-0 !w-full\"\ntabIndex={-1}>\n\n### Loops Setup\n\n<Steps>\n\n### Database Adapter\n\nPlease make sure you've [setup a database adapter](/getting-started/database), as mentioned earlier,\na database is required for passwordless login to work as verification tokens need to be stored.\n\n### Create your Transactional Email Template on Loops\n\nLoops have provided a super handy [guide](https://loops.so/docs/transactional/guide) to help you get started with creating your transactional email template.\nThis provider only passes one data varaiable into the template, `url` which is the magic link to sign in. This is case sensitive, so make sure you use `url` in your template. <br/>\nOn the last page of Template creation, you'll need to copy the `TRANSACTIONAL ID`. If you skipped this step, don't worry, you can get this at any from the Template edit page.\n\n### Create an API Key on Loops\n\nYou'll need to create an API key to authenticate with Loops. This key should be kept secret and not shared with anyone.\nYou can Generate a key by going to the [API Settings Page](https://app.loops.so/settings?page=api) and clicking Generate.\nYou should name the key something that makes sense to you, like \"Auth.js\".\n\n### Setup Environment Variables\n\nTo implement Loops, you need to set up the following environment variables. You should have these from the previous steps.\n\n```bash filename=\".env\"\nAUTH_LOOPS_KEY=abc123\nAUTH_LOOPS_TRANSACTIONAL_ID=def456\n```\n\n### Setup Provider\n\nLet's enable `Loops` as a sign-in option for our Auth.js configuration. You'll have to import the `Loops` provider from the package and pass it to the providers array we set up earlier in the Auth.js config file:\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Loops from \"next-auth/providers/loops\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Loops({\n      apiKey: process.env.AUTH_LOOPS_KEY,\n      transactionalId: process.env.AUTH_LOOPS_TRANSACTIONAL_ID,\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport Loops from \"@auth/sveltekit/providers/loops\"\nimport {\n  AUTH_LOOPS_KEY,\n  AUTH_LOOPS_TRANSACTIONAL_ID,\n} from \"$env/static/private\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    Loops({\n      apiKey: AUTH_LOOPS_KEY,\n      transactionalId: AUTH_LOOPS_TRANSACTIONAL_ID,\n    }),\n  ],\n})\n```\n\n```ts filename=\"./src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n\n### Add Signin Button\n\nNext, we add a signin button somewhere in your application like the Navbar. This will send an email to the user containing the magic link to sign in.\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"./components/sign-in.tsx\"\nimport { signIn } from \"../../auth.ts\"\n\nexport function SignIn() {\n  return (\n    <form\n      action={async (formData) => {\n        \"use server\"\n        await signIn(\"loops\", formData)\n      }}\n    >\n      <input type=\"text\" name=\"email\" placeholder=\"Email\" />\n      <button type=\"submit\">Sign in with Loops</button>\n    </form>\n  )\n}\n```\n\n</Code.Next>\n<Code.Svelte>\n\n```ts filename=\"src/routes/+page.svelte\"\n\n<script lang=\"ts\">\n  import { SignIn } from \"@auth/sveltekit/components\"\n</script>\n\n<div>\n  <nav>\n    <img src=\"/img/logo.svg\" alt=\"Company Logo\" />\n    <SignIn provider=\"loops\"/>\n  </nav>\n</div>\n\n```\n\n</Code.Svelte>\n</Code>\n\n### Signin\n\nStart your application, click on the signin button we just added, and you should see Auth.js built-in sign in page with the option to sign in with your email.\nA user can enter their email, click \"Sign in with Loops\", and receive their beautifully formatted signin email.\nClicking on the link in the email will redirect the user to your application, landing already authenticated!\n\n</Steps>\n</RichTabs.Content>\n<RichTabs.Content\n  orientation=\"vertical\"\n  value=\"mailgun\"\n  className=\"h-full !border-0 !w-full\"\n  tabIndex={-1}\n>\n\n### Mailgun Setup\n\n<Steps>\n\n### Database Adapter\n\nPlease make sure you've [setup a database adapter](/getting-started/database), as mentioned earlier,\na database is required for passwordless login to work as verification tokens need to be stored.\n\n### Setup Environment Variables\n\nAuth.js will automatically pick up these if formatted like the example above.\nYou can [also use a different name for the environment variables](/guides/environment-variables#oauth-variables) if needed, but then you’ll need to pass them to the provider manually.\n\n```bash filename=\".env\"\nAUTH_MAILGUN_KEY=abc123\n```\n\n### Setup Provider\n\nLet’s enable `Mailgun` as a sign in option in our Auth.js configuration.\nYou’ll have to import the `Mailgun` provider from the package and pass it to the providers array we setup earlier in the Auth.js config file:\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Mailgun from \"next-auth/providers/mailgun\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Mailgun],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Mailgun from \"@auth/qwik/providers/mailgun\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Mailgun],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport Mailgun from \"@auth/sveltekit/providers/mailgun\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Mailgun],\n})\n```\n\n```ts filename=\"./src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n\n### Add Signin Button\n\nNext, we can add a signin button somewhere in your application like the Navbar. This will send an email to the user containing the magic link to sign in.\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"./components/sign-in.tsx\"\nimport { signIn } from \"../../auth.ts\"\n\nexport function SignIn() {\n  return (\n    <form\n      action={async (formData) => {\n        \"use server\"\n        await signIn(\"mailgun\", formData)\n      }}\n    >\n      <input type=\"text\" name=\"email\" placeholder=\"Email\" />\n      <button type=\"submit\">Signin with Mailgun</button>\n    </form>\n  )\n}\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"./components/sign-in.tsx\"\nimport { component$ } from \"@builder.io/qwik\"\nimport { useSignIn } from \"./plugin@auth\"\n\nexport default component$(() => {\n  const signInSig = useSignIn()\n\n  return (\n    <button\n      onClick$={() => signInSig.submit({ redirectTo: \"/\" })}\n    >\n      SignIn\n    </button>\n  )\n})\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```html filename=\"src/routes/+page.svelte\"\n<script lang=\"ts\">\n  import { SignIn } from \"@auth/sveltekit/components\"\n</script>\n\n<div>\n  <nav>\n    <img src=\"/img/logo.svg\" alt=\"Company Logo\" />\n    <SignIn provider=\"mailgun\" />\n  </nav>\n</div>\n```\n\n</Code.Svelte>\n</Code>\n\n### Signin\n\nStart your application, once the user enters their Email and clicks on the signin button, they'll be redirected to a page that asks them to check their email. When they click on the link in their email, they will be signed in.\n\n</Steps>\n\n<Callout type=\"info\">\n  Check our [Customising magic links\n  emails](/getting-started/providers/mailgun#customization) to learn how to\n  change the look and feel of the emails the user receives to sign in.\n</Callout>\n\nFor more information on this provider go to the [Mailgun docs page](/getting-started/providers/mailgun).\n\n</RichTabs.Content>\n</RichTabs>\n</div>\n"
  },
  {
    "path": "docs/pages/getting-started/authentication/oauth.mdx",
    "content": "---\ntitle: OAuth Providers\n---\n\nimport { OAuthProviderSelect } from \"@/components/OAuthProviderInstructions\"\n\n# OAuth\n\nAuth.js comes with over 80 providers preconfigured. We constantly test ~20 of the most popular ones, by having them enabled and actively used in our [example application](https://next-auth-example.vercel.app). You can choose a provider below to get a walk-through, or find your provider of choice in the sidebar for further details.\n\n<OAuthProviderSelect />\n"
  },
  {
    "path": "docs/pages/getting-started/authentication/webauthn.mdx",
    "content": "---\ntitle: WebAuthn\n---\n\nimport { Steps, Callout } from \"nextra/components\"\n\n# WebAuthn (Passkeys)\n\n<Callout type=\"warning\">\n  The WebAuthn / Passkeys provider is experimental and not recommended for\n  production use.\n</Callout>\n\nThe WebAuthn provider requires changes to all of the framework integration as well\nas any database adapter that plans to support it. Therefore, the WebAuthn provider\nis currently only supported in the following framework integration and database adapters.\nSupport for more frameworks and adapters are coming soon.\n\n- `next-auth@5.0.0-beta.8` or above\n- `@auth/prisma-adapter@1.3.0` or above\n- `node@20.0.0` or above\n\n<Steps>\n\n### Install peer dependencies\n\n```bash npm2yarn\nnpm install @simplewebauthn/server@9.0.3 @simplewebauthn/browser@9.0.1\n```\n\nThe `@simplewebauthn/browser` peer dependency **is only required for custom signin pages**. If you're using the Auth.js default pages, you can skip installing that peer dependency.\n\n### Apply the required schema Migrations\n\nThis is the raw SQL migration for PostgreSQL, for more details including example migrations for other databases, check out the updated Prisma schemas at the [`@auth/prisma-adapter` docs](/getting-started/adapters/prisma).\n\nIn short, the Passkeys provider requires an additional table called `Authenticator`.\n\n```sql filename=\"./migration/add-webauthn-authenticator-table.sql\"\n-- CreateTable\nCREATE TABLE \"Authenticator\" (\n    \"credentialID\" TEXT NOT NULL,\n    \"userId\" TEXT NOT NULL,\n    \"providerAccountId\" TEXT NOT NULL,\n    \"credentialPublicKey\" TEXT NOT NULL,\n    \"counter\" INTEGER NOT NULL,\n    \"credentialDeviceType\" TEXT NOT NULL,\n    \"credentialBackedUp\" BOOLEAN NOT NULL,\n    \"transports\" TEXT,\n    PRIMARY KEY (\"userId\", \"credentialID\"),\n    CONSTRAINT \"Authenticator_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES \"User\" (\"id\") ON DELETE CASCADE ON UPDATE CASCADE\n);\n\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"Authenticator_credentialID_key\" ON \"Authenticator\"(\"credentialID\");\n```\n\n### Update Auth.js Configuration\n\nAdd the `Passkeys` provider to your configuration. Also make sure you're using a compatible database adapter.\n\n```ts filename=\"./auth.ts\"\nimport Passkey from \"next-auth/providers/passkey\"\nimport { PrismaAdapter } from \"@auth/prisma-adapter\"\nimport { PrismaClient } from \"@prisma/client\"\n\nconst prisma = new PrismaClient()\n\nexport default {\n  adapter: PrismaAdapter(prisma),\n  providers: [Passkey],\n  experimental: { enableWebAuthn: true },\n}\n```\n\nIf you're using the built-in Auth.js pages, then you are good to go! Navigating to your `/signin` route should include a \"Signin with Passkeys\" button.\n\n### Custom Pages\n\nIf you're using a custom signin page, you can leverage the `next-auth` `signIn` function to initiate WebAuthn registration and login flows with the following code.\n\n<Callout>\n  When using the WebAuthn `signIn` function, you'll also need the\n  `@simplewebauth/browser` peer dependency installed.\n</Callout>\n\n```ts filename=\"app/login/page.tsx\"\n\"use client\"\n\nimport { useSession } from \"next-auth/react\"\nimport { signIn } from \"next-auth/webauthn\"\n\nexport default function Login() {\n  const { data: session, update, status } = useSession()\n\n  return (\n    <div>\n      {status === \"authenticated\" ? (\n        <button onClick={() => signIn(\"passkey\", { action: \"register\" })}>\n          Register new Passkey\n        </button>\n      ) : status === \"unauthenticated\" ? (\n        <button onClick={() => signIn(\"passkey\")}>Sign in with Passkey</button>\n      ) : null}\n    </div>\n  )\n}\n```\n\n</Steps>\n"
  },
  {
    "path": "docs/pages/getting-started/authentication.mdx",
    "content": "import { Screenshot } from \"@/components/Screenshot\"\nimport { Accordion, Accordions } from \"@/components/Accordion\"\n\n# Authentication\n\nAt this point, you need to decide how you're gonna authenticate users in your application. Auth.js supports four main authentication paradigms.\n\n<Accordions>\n<Accordion title=\"Why does Auth.js recommend OAuth as the primary authentication method for your application?\">\n\nOAuth services spend significant amounts of money, time, and engineering effort to build abuse detection (_bot-protection, rate-limiting_), password management (_password reset, credential stuffing, rotation_), data security (_encryption/salting, strength validation_), and much more. It is likely that your application would benefit from leveraging these battle-tested solutions rather than try to rebuild them from scratch. Or if you don't want to pay for an OAuth service, we support many self-hosted OAuth providers like [Keycloak](/getting-started/providers/keycloak).\n\n</Accordion>\n<Accordion title=\"Do I need a database?\">\n\nAuth.js by default saves the session in a cookie using encrypted [JSON Web Tokens](https://jwt.io/) so that you don't need to setup a database. However, if you want to persist user data you can set up a database by using one of our official database adapters or create your own.\n\n</Accordion>\n<Accordion title=\"Can I set up more than one authentication method?\">\n\nYes! You can setup as many authentication methods as you'd like. However, when using a database, if you have multiple providers set up Auth.js will attempt to link them together. For example, if a user has already signed in with GitHub, and therefore has a `User` and `Account` table entry associated with that Email, and they then attempt to signin with another method using the same email address, then the two accounts will be linked. See the [FAQ](/concepts#security) for more information on account linking.\n\n</Accordion>\n</Accordions>\n\nOnce you have decided how to authenticate users in your application, click on your authentication method of choice under \"Authentication\" in the sidebar to continue.\n"
  },
  {
    "path": "docs/pages/getting-started/database.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Link } from \"@/components/Link\"\nimport manifest from \"@/data/manifest.json\"\n\n# Database Adapters\n\n<Callout type=\"info\">\n  Auth.js integrations save sessions in a cookie by default. Therefore, setting\n  up a database is optional. However, if you want to persist user information in\n  your own database, or you want to implement certain flows, you will need to\n  use a Database Adapter.\n</Callout>\n\n**Database Adapters** are the bridge we use to connect Auth.js to your database. For instance, [when implementing magic links](/getting-started/authentication/email), the Email provider will require you to setup a database adapter to be able to store the [verification tokens](/concepts/database-models#verificationtoken) present on the links.\n\n## Official Adapters\n\nBelow is a list of official adapters that are distributed as their own packages under the `@auth/`\nnamespace. Their source code is available in the [`nextauthjs/next-auth`\nmonorepo](https://github.com/nextauthjs/next-auth/tree/main/packages). If you're going to create a database adapter, please make sure you familiarise yourself [with the models](/concepts/database-models) Auth.js expects to be present and check out our \"[creating a database adapter](/guides/creating-a-database-adapter)\" guide.\n\n<div className=\"mb-12 mt-8 grid w-full grid-cols-2 gap-x-4 gap-y-4 sm:grid-cols-3 lg:grid-cols-4\">\n  {Object.entries(manifest.adapters).map(([value, label]) => (\n    <Link\n      href={`/getting-started/adapters/${value}`}\n      key={value}\n      className=\"flex h-32 w-36 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950\"\n    >\n      <img src={`/img/adapters/${value}.svg`} className=\"mt-2 h-12\" />\n      <div className=\"text-center text-sm\">{label}</div>\n    </Link>\n  ))}\n</div>\n\n<Callout>\n  If you don't find an adapter for your database or service of choice, you can\n  create one yourself. Have a look at our guide on [how to create a database\n  adapter](/guides/creating-a-database-adapter). If you create a new adapter,\n  we'd love it if you [opened a\n  PR](/guides/creating-a-database-adapter#official-adapter-guidelines) to share\n  it with everyone!\n</Callout>\n\n## Models\n\nThis is a generic ER Diagram of what the full database schema should look like. Your database adapter of choice will include a template schema with more details for applying this schema to the underlying database. For more details, check out our [database models](/concepts/database-models) documentation. Please note, that the entire schema is not required for every use-case, for more details check out our [database adapters guide](/guides/creating-a-database-adapter).\n\n```mermaid\n%%{init: {'theme':'neutral'}}%%\nerDiagram\n    Account }o--|| User : \"\"\n    Account {\n        string userId\n        string type\n        string provider\n        string providerAccountId\n        string refresh_token\n        string access_token\n        int expires_at\n        string token_type\n        string scope\n        string id_token\n        string session_state\n    }\n    Session }o--|| User : \"\"\n    Session {\n        string userId\n        string sessionToken\n        timestamp expires\n    }\n    VerificationToken }o--|| User : \"\"\n    VerificationToken {\n        string identified\n        string token\n        timestamp expires\n    }\n    User {\n        string id\n        string name\n        string email\n        timestamp emailVerified\n        string image\n    }\n```\n"
  },
  {
    "path": "docs/pages/getting-started/deployment.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Accordion, Accordions } from \"@/components/Accordion\"\n\n# Deployment\n\n## Environment Variables\n\n<Callout>\n  For consistency, we recommend prefixing all Auth.js environment variables with\n  `AUTH_`. This way we can better autodetect them, and they can also be\n  distinguished from other environment variables more easily.\n</Callout>\n\nAuth.js libraries require you to set an `AUTH_SECRET` environment variable. This is used to encrypt cookies and tokens. It should be a cryptographically secure random string of at least 32 characters:\n\n```bash npm2yarn\nnpm exec auth secret\n```\n\nIf you are using an [OAuth Provider](/concepts/oauth), your provider will provide you with a **Client ID** and **Client Secret** that you will need to set as environment variables as well (in the case of an OIDC provider, like Auth0, a third `issuer` value might be also required, refer to the provider's specific documentation).\n\n<Callout type=\"info\">\nAuth.js supports **environment variable inference**, meaning that if you name your provider environment variables following a specific syntax, you won't need to explicitly pass them to the providers in your configuration.\n\nClient ID's and client secrets should be named `AUTH_[PROVIDER]_ID` and `AUTH_[PROVIDER]_SECRET`. If your provider requires an issuer, that should be named `AUTH_[PROVIDER]_ISSUER`. For example:\n\n```bash\nAUTH_OKTA_ID=abc\nAUTH_OKTA_SECRET=abc\nAUTH_OKTA_ISSUER=abc\n```\n\nFor more information, check out our [environment variables](/guides/environment-variables) page.\n\n</Callout>\n\n### `AUTH_SECRET`\n\nThis is the only strictly required environment variable. It is the secret used to encode the JWT and encrypt things in transit. As mentioned above, we recommend at least a 32 character random string. This can be generated via the CLI with `npm exec auth secret` or via openssl with `openssl rand -base64 33`.\n\n### `AUTH_TRUST_HOST`\n\nWhen deploying your application behind a reverse proxy, you'll need to set `AUTH_TRUST_HOST` equal\nto `true`. This tells Auth.js to trust the `X-Forwarded-Host` header from the reverse proxy. Auth.js will automatically infer this to be true if we detect the environment variable indicating that your application is running on one of the supported hosting providers. Currently `VERCEL` and `CF_PAGES` (Cloudflare Pages) are supported.\n\n### `AUTH_URL`\n\nThis environment variable is mostly unnecessary with v5 as the host is inferred from the request headers. However, if you are using a different base path, you can set this environment variable as well. For example, `AUTH_URL=http://localhost:3000/web/auth` or `AUTH_URL=https://company.com/app1/auth`\n\n### `AUTH_REDIRECT_PROXY_URL`\n\n> **_NOTE:_** Some providers (eg Apple) do not support [redirect proxy](https://github.com/nextauthjs/next-auth/blob/3ec06842682a31e53fceabca701a362abda1e7dd/packages/core/src/lib/utils/providers.ts#L48) usage.\n\nThis environment variable is designed for advanced use-cases only, when using Auth.js as a proxy for preview deploys, for example. For more details, see the [securing preview deploys](#securing-a-preview-deployment) section below.\n\n## Serverless\n\n1. Create the required [environment variables](#environment-variables) for your desired environments. Don't forget to also add the required environment variables for your provider(s) of choice (i.e. OAuth `clientId` / `clientSecret`, etc.).\n2. When using an OAuth provider, make sure the callback URL for your production URL is setup correctly. Many OAuth providers will only allow you to set 1 `callbackUrl` per OAuth application. In which case, you'll need to create separate applications for each environment (development, production, etc.). Other providers, like Google, allow you to add many `callbackUrl`s to one application.\n   - By default, the callbackUrl for `next-auth` (Next.js) applications should look something like this: `https://company.com/api/auth/callback/[provider]` (replace `company.com` with your domain and `provider` with the provider name, i.e. `github`).\n   - All other frameworks (`@auth/sveltekit`, `@auth/express`, etc.), by default, will use the path `/auth/callback/[provider]`.\n3. Deploy! After having setup those two prerequisites, you should be able to deploy and run your Auth.js application on Netlify, Vercel, etc.\n\n<Callout type=\"warning\">\n  If you are storing users in a [database](/getting-started/database), we\n  recommend using a different OAuth app for development/production so that you\n  don't mix your test and production user base.\n</Callout>\n\n## Observability\n\nTo pass on your current user's details on to your observability tools, you can use the callbacks provided by Auth.js. For example, in the `session` callback, you could pass the `user.id` on to Sentry.\n\n```ts filename=\"auth.ts\"\nimport * as Sentry from \"@sentry/browser\"\nimport NextAuth from \"next-auth\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  callbacks: {\n    session({ session, user }) {\n      const scope = Sentry.getCurrentScope()\n\n      scope.setUser({\n        id: user.id,\n        email: user.email,\n      })\n\n      return session\n    },\n  },\n})\n```\n\n## Self-hosted\n\nAuth.js can also be deployed anywhere you can deploy your framework of choice. Check out the framework's documentation on self-hosting.\n\n### Docker\n\nIn a Docker environment, make sure to set either `trustHost: true` in your Auth.js configuration or the `AUTH_TRUST_HOST` environment variable to `true`.\n\nOur example application is also hosted via Docker [here](https://nextjs-docker-example.authjs.dev) (see the [source code](https://github.com/nextauthjs/next-auth-example)). Below is an example `Dockerfile` for a Next.js application using Auth.js.\n\n```docker filename=\"Dockerfile\"\n# syntax=docker/dockerfile:1\nFROM node:20-alpine AS base\n\n# Install dependencies only when needed\nFROM base AS deps\n# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.\nRUN apk add --no-cache libc6-compat\nWORKDIR /app\n\n# Install dependencies\nCOPY package.json pnpm-lock.yaml* ./\nRUN corepack enable pnpm && pnpm i --frozen-lockfile\n\n# Rebuild the source code only when needed\nFROM base AS builder\nWORKDIR /app\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY . .\n\n# Next.js collects completely anonymous telemetry data about general usage.\n# Learn more here: https://nextjs.org/telemetry\n# Uncomment the following line in case you want to disable telemetry during the build.\n# ENV NEXT_TELEMETRY_DISABLED 1\n\nRUN corepack enable pnpm && pnpm build\n\n# Production image, copy all the files and run next\nFROM base AS runner\nWORKDIR /app\n\nENV NODE_ENV production\n# Uncomment the following line in case you want to disable telemetry during runtime.\n# ENV NEXT_TELEMETRY_DISABLED 1\n\nRUN addgroup --system --gid 1001 nodejs\nRUN adduser --system --uid 1001 nextjs\n\nCOPY --from=builder /app/public ./public\n\n# Set the correct permission for prerender cache\nRUN mkdir .next\nRUN chown nextjs:nodejs .next\n\n# Automatically leverage output traces to reduce image size\n# https://nextjs.org/docs/advanced-features/output-file-tracing\nCOPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./\nCOPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static\n\nUSER nextjs\n\nEXPOSE 3000\n\nENV PORT 3000\nENV HOSTNAME \"0.0.0.0\"\n\n# server.js is created by next build from the standalone output\n# https://nextjs.org/docs/pages/api-reference/next-config-js/output\nCMD [\"node\", \"server.js\"]\n```\n\n## Securing a preview deployment\n\n> **_NOTE:_** Some providers (eg Apple) do not support [redirect proxy](https://github.com/nextauthjs/next-auth/blob/3ec06842682a31e53fceabca701a362abda1e7dd/packages/core/src/lib/utils/providers.ts#L48) usage.\n\nMost OAuth providers cannot be configured with multiple callback URLs or using a wildcard.\n\nHowever, Auth.js **supports Preview deployments**, even **with most OAuth providers**. The idea is to have one deployment which proxies authentication requests to the dynamic URLs of your main application. So you could have 1 stable deployment, like at `auth.company.com` where you would point all your OAuth provider's `callbackUrl`s, and this application would then, upon successful authentication, redirect the user back to the preview deploy URL, like `https://git-abc123-myapp.vercel.app`. Follow these steps to get started with securing preview deploys with Auth.js.\n\n1. Determine a stable deployment URL. For example, a deployment whose URL does not change between builds, for example. `auth.yourdomain.com` (using a subdomain is not a requirement, this can be the main site's URL too, for example.)\n2. In **both the preview and stable environment**, set `AUTH_REDIRECT_PROXY_URL` to that stable deployment URL, including the path from where Auth.js handles the routes. Eg.: (`https://auth.yourdomain.com/api/auth`). If the variable is not set in the stable environment, the proxy functionality will not be enabled!\n3. Update the `callbackUrl` in your OAuth provider's configuration to use the stable deployment URL. For example, for GitHub it would be `https://auth.yourdomain.com/api/auth/callback/github`.\n\nFun fact: all of our example apps are using the proxy functionality!\n\n<Callout type=\"info\">\n  To support preview deployments, the `AUTH_SECRET` value needs to be the same\n  for the stable deployment and deployments that will need OAuth support.\n</Callout>\n\n<details>\n<summary>\n<b>How does this work?</b>\n</summary>\nTo support preview deployments, Auth.js requires a deployment URL that is stable across deploys as a redirect proxy server.\n\nIt will redirect the OAuth callback request to the preview deployment URL, but only when the `AUTH_REDIRECT_PROXY_URL` environment variable is set.\n\nWhen a user initiates an OAuth sign-in flow on a preview deployment, we save its URL in the `state` query parameter but set the `redirect_uri` to the stable deployment.\n\nThen, the OAuth provider will redirect the user to the stable URL mentioned above, which will verify the `state` parameter and redirect the user to the preview deployment URL if the `state` is valid. This is secured by relying on the same server-side `AUTH_SECRET` for the stable deployment and the preview deployment.\n\n</details>\n"
  },
  {
    "path": "docs/pages/getting-started/index.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { ListDisclosure } from \"@/components/ListDisclosure\"\nimport { Link } from \"@/components/Link\"\nimport { Screenshot } from \"@/components/Screenshot\"\nimport { Plus, ArrowSquareOut, GithubLogo, Flask } from \"@/icons\"\nimport manifest from \"@/data/manifest.json\"\nimport { FrameworkLink } from \"@/components/FrameworkLink\"\n\n# What is Auth.js?\n\nAuth.js is a <abbr title=\"Designed to work seamlessly across different JavaScript runtime environments without relying on specific features or behaviors of any particular runtime\">runtime agnostic</abbr> library based on standard Web APIs that integrates deeply with multiple modern JavaScript frameworks to provide an authentication experience that's simple to get started with, easy to extend, and always private and secure!\n\nThis documentation covers `next-auth@5.0.0-beta` and later and all other frameworks under the `@auth/*` namespace. Documentation for `next-auth@4.x.y` can still be found at [next-auth.js.org](https://next-auth.js.org).\n\nSelect your framework of choice to get started, or view the example application deployment or repository with the buttons below.\n\n<div className=\"mb-12 mt-8 flex w-full flex-wrap items-start justify-around gap-2\">\n  <FrameworkLink\n    id=\"nextjs\"\n    name=\"Next.js\"\n    demo=\"https://next-auth-example.vercel.app/\"\n    repo=\"https://github.com/nextauthjs/next-auth-example\"\n    isExperimental={false}\n    isInvert={true}\n  />\n  <FrameworkLink\n    id=\"qwik\"\n    name=\"Qwik\"\n    demo=\"https://qwik-auth-example.vercel.app/\"\n    repo=\"https://github.com/nextauthjs/qwik-auth-example\"\n  />\n  <FrameworkLink\n    id=\"sveltekit\"\n    name=\"SvelteKit\"\n    demo=\"https://sveltekit-auth-example.vercel.app/\"\n    repo=\"https://github.com/nextauthjs/sveltekit-auth-example\"\n  />\n  <FrameworkLink\n    id=\"express\"\n    name=\"Express\"\n    demo=\"https://express-auth-example.vercel.app/\"\n    repo=\"https://github.com/nextauthjs/express-auth-example\"\n    isInvert={true}\n  />\n  <Link\n    href=\"/guides/creating-a-framework-integration\"\n    className=\"relative flex h-28 w-28 flex-col flex-wrap items-center justify-between rounded-lg border border-solid border-slate-200 bg-white p-4 !no-underline shadow-sm transition duration-300 hover:bg-neutral-100 dark:border-neutral-800 dark:bg-neutral-900 hover:dark:bg-neutral-950\"\n  >\n    <Plus className=\"size-9\" />\n    <div className=\"mt-3 text-sm\">Add New</div>\n  </Link>\n</div>\n\nCheck the [integrations page](/getting-started/integrations) for all supported packages. We are working on supporting more frameworks, but you can create your own or\nhelp us create one for your favorite framework.\n\n## Authentication methods\n\nThere are 4 ways to authenticate users with Auth.js:\n\n- [OAuth authentication](/getting-started/authentication/oauth) (_Sign in with Google, GitHub, LinkedIn, etc..._)\n- [Magic Links](/getting-started/authentication/email) (_Email Provider like Forward Email, Resend, Sendgrid, Nodemailer etc..._)\n- [Credentials](/getting-started/authentication/credentials) (_Username and Password, Integrating with external APIs, etc..._)\n- [WebAuthn](/getting-started/authentication/webauthn) (_Passkeys, etc..._)\n\n### Official Providers\n\n<ListDisclosure limit={8}>\n  {Object.entries(manifest.providersOAuth).map(([id, name]) => (\n    <Link\n      href={`/getting-started/providers/${id}`}\n      key={name}\n      className=\"flex h-32 w-36 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 !no-underline shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950\"\n    >\n      <img src={`/img/providers/${id}.svg`} className=\"mt-2 w-11\" />\n      <div className=\"text-center text-sm\">{name}</div>\n    </Link>\n  ))}\n</ListDisclosure>\n\n### Supported Databases\n\nOptionally, Auth.js can be integrated with an external database via Database adapters, in case you need or want to store user data.\n\n<ListDisclosure limit={8}>\n  {Object.entries(manifest.adapters).map(([value, label]) => (\n    <Link\n      href={`/getting-started/adapters/${value}`}\n      key={value}\n      className=\"flex h-32 w-36 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 !no-underline shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950\"\n    >\n      <img src={`/img/adapters/${value}.svg`} className=\"mt-2 h-12\" />\n      <div className=\"text-center text-sm\">{label}</div>\n    </Link>\n  ))}\n</ListDisclosure>\n"
  },
  {
    "path": "docs/pages/getting-started/installation.mdx",
    "content": "import { Steps, Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<Steps>\n### Installing Auth.js\n\nStart by installing the appropriate package for your framework.\n\n<Code>\n  <Code.Next>\n    \n    ```bash npm2yarn\n    npm install next-auth@beta\n    ```\n\n  </Code.Next>\n  <Code.Qwik>\n\n    ```bash npm2yarn\n    npm run qwik add auth\n    ```\n\n  </Code.Qwik>\n  <Code.Svelte>\n  \n    ```bash npm2yarn\n    npm install @auth/sveltekit\n    ```\n  </Code.Svelte>\n  <Code.Express>\n  \n    ```bash npm2yarn\n    npm install @auth/express\n    ```\n  \n  </Code.Express>\n</Code>\n\n<Callout type=\"info\">\n  Installing `@auth/core` is not necessary, as a user you should never have to\n  interact with `@auth/core`.\n</Callout>\n\n### Setup Environment\n\nThe only environment variable that is mandatory is the `AUTH_SECRET`. This is a random value used by the library to encrypt tokens and email\nverification hashes. (See [Deployment](/getting-started/deployment) to learn more). You can generate one via the official [Auth.js CLI](https://cli.authjs.dev) running:\n\n```bash\nnpx auth secret\n```\n\nThis will also add it to your `.env` file, respecting the framework conventions (eg.: Next.js' `.env.local`).\n\n### Configure\n\nNext, create the Auth.js config file and object. This is where you can control the behaviour of the library and specify custom authentication logic, adapters, etc. We recommend all frameworks to create an `auth.ts` file in the project. In this file we'll pass in all the options to the framework specific initialization function and then export the route handler(s), signin and signout methods, and more.\n\n<Callout type=\"info\">\n  You can name this file whatever you want and place it wherever you like, these\n  are just conventions we've come up with.\n</Callout>\n\n<Code>\n  <Code.Next>\n\n1. Start by creating a new `auth.ts` file at the root of your app with the following content.\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\n\nexport const { handlers, signIn, signOut, auth } = NextAuth({\n  providers: [],\n})\n```\n\n2. Add a Route Handler under `/app/api/auth/[...nextauth]/route.ts`.\n\n<Callout>\n  This file must be an App Router Route Handler, however, the rest of your app\n  can stay under `page/` if you'd like.\n</Callout>\n\n```ts filename=\"./app/api/auth/[...nextauth]/route.ts\"\nimport { handlers } from \"@/auth\" // Referring to the auth.ts we just created\nexport const { GET, POST } = handlers\n```\n\n3. Add optional Proxy to keep the session alive, this will update the session expiry every time its called.\n\n<Callout type=\"info\">\n  As of Next.js 16, `middleware.ts` has been renamed to `proxy.ts`. If you are\n  using an older version of Next.js, use `middleware.ts` with `export { auth as middleware }` instead.\n</Callout>\n\n```ts filename=\"./proxy.ts\"\nexport { auth as proxy } from \"@/auth\"\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n1. Start by creating a new `plugin@auth.ts` file in your `src/routes` directory with the following content.\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [...],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n1. Start by creating a new `auth.ts` file in your `src/` directory with the following content.\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\n\nexport const { handle } = SvelteKitAuth({\n  providers: [],\n})\n```\n\n2. Re-export the `handle` method in SvelteKit's `src/hooks.server.ts` file.\n\n```ts filename=\"./src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n3. That handle function adds an `auth()` method onto our `event.locals`, which is available in any `+layout.server.ts` or `+page.server.ts` file. Therefore, we can access the session in our `load` function like this.\n\n```ts filename=\"./src/routes/+layout.server.ts\" {4}\nimport type { LayoutServerLoad } from \"./$types\"\n\nexport const load: LayoutServerLoad = async (event) => {\n  const session = await event.locals.auth()\n\n  return {\n    session,\n  }\n}\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n1. Start by importing `ExpressAuth` and adding the handler to the auth route.\n\n```ts filename=\"./src/routes/auth.route.ts\" {1, 9}\nimport { ExpressAuth } from \"@auth/express\"\nimport express from \"express\"\n\nconst app = express()\n\n// If your app is served through a proxy\n// trust the proxy to allow us to read the `X-Forwarded-*` headers\napp.set(\"trust proxy\", true)\napp.use(\"/auth/*\", ExpressAuth({ providers: [] }))\n```\n\nNote this creates the Auth.js API, but does not yet protect resources. Continue on to [protecting resources](/getting-started/session-management/protecting) for more details.\n\n  </Code.Express>\n</Code>\n\n### Setup Authentication Methods\n\nWith that, the basic setup is complete! Next we'll setup the first authentication methods and fill out that `providers` array.\n\n</Steps>\n"
  },
  {
    "path": "docs/pages/getting-started/integrations.mdx",
    "content": "import { Callout } from \"nextra/components\"\n\n# Integrations\n\nHere are the state of planned and released integrations under the `@auth/*` and `@next-auth/*` scope, as well as `next-auth`. It also includes community created and maintained integrations. Integrations listed as \"Planned\" are something we'd love help with! See the [help needed](#help-needed) section below.\n\n<Callout>\n  Is your framework not supported? You can easily contribute by creating a\n  framework integration by [following this\n  guide](/guides/creating-a-framework-integration).\n</Callout>\n\n<Callout type=\"info\">\n  Note that because of preventing breaking changes on package imports,\n  `next-auth` is the only framework package which is not named following the\n  `@auth/*` convention. This library initially was born as `next-auth` but [over\n  the years has evolved](/contributors#history) to be framework agnostic.\n</Callout>\n\nFramework and database integrations are all based on the [Auth.js Core library](/reference/core). In most cases, you will not interact with this package directly, as it is intended for library authors.\n\n| Frameworks                            | Status                                                                                    |\n| ------------------------------------- | ----------------------------------------------------------------------------------------- |\n| NextAuth.js (`next-auth`)             | [v5 (beta)](https://nextjs.authjs.dev), [v4 (maintenance mode)](https://next-auth.js.org) |\n| Astro Auth (`@auth/astro`)            | [Open PR](https://github.com/nextauthjs/next-auth/pull/9856)                              |\n| Express Auth (`@auth/express`)        | [Experimental Release](https://express.authjs.dev)                                        |\n| Fastify Auth (`@auth/fastify`)        | [Open PR](https://github.com/nextauthjs/next-auth/pull/9587)                              |\n| Nuxt Auth (`@auth/nuxt`)              | [Open PR](https://github.com/nextauthjs/next-auth/pull/10684)                             |\n| Qwik Auth (`@auth/qwik`)              | [Released](https://qwik.authjs.dev)                                                       |\n| Remix Auth (`@auth/remix`)            | [Open PR](https://github.com/nextauthjs/next-auth/pull/6767)                              |\n| SolidStart Auth (`@auth/solid-start`) | [Experimental Release](https://authjs.dev/reference/solid-start)                          |\n| SvelteKit Auth (`@auth/sveltekit`)    | [Experimental Release](https://sveltekit.authjs.dev)                                      |\n\n| Databases              | Status                                                                  |\n| ---------------------- | ----------------------------------------------------------------------- |\n| `@auth/*-adapter`      | Released. Fully compatible with `next-auth` and all `@auth/*` libraries |\n| `@next-auth/*-adapter` | Maintenance has stopped. Update to `@auth/*-adapter`                    |\n\n## Community Integrations\n\nThe community has published some great integrations / client packages for various frameworks and\nlibraries. We'd love to make some packages official in the future, if you're responsible for any of\nthem and are interested in collaborating, please do not hesitate to reach out!\n\n| Client     | Links                                                                                       |\n| ---------- | ------------------------------------------------------------------------------------------- |\n| Hono.js    | [Auth.js middleware](https://github.com/honojs/middleware/tree/main/packages/auth-js)       |\n| Rakkas     | [Auth.js Integration Example](https://github.com/rakkasjs/rakkasjs/tree/main/examples/auth) |\n| SolidStart | [`@solid-mediakit/auth`](https://www.npmjs.com/package/@solid-mediakit/auth)                |\n| Astro      | [`auth-astro`](https://github.com/nowaythatworked/auth-astro)                               |\n| Nuxt       | [`@sidebase/nuxt-auth`](https://auth.sidebase.io)                                           |\n\n### Help needed\n\nIn case you are a maintainer of a package that uses `@auth/core`, feel free to [reach out to\nBalázs](https://twitter.com/balazsorban44) or [info@authjs.dev](mailto:info@authjs.dev), if you want to collaborate on making it an official package, maintained in our repository. If you are interested in bringing `@auth/core` support to your favorite framework, we would love to hear from you!\n"
  },
  {
    "path": "docs/pages/getting-started/migrate-to-better-auth.mdx",
    "content": "---\ntitle: Migrating from Auth.js to Better Auth\ndescription: A step-by-step guide to transitioning from Auth.js to Better Auth.\n---\n\nimport { Callout, Tabs } from \"nextra/components\"\n\n## Introduction\n\nIn this guide, we'll walk through the steps to migrate a project from Auth.js to [Better Auth](https://www.better-auth.com). Since these projects have different design philosophies, the migration requires careful planning and work. If your current setup is working well, there's no urgent need to migrate. Better Auth team continues to handle security patches and critical issues for Auth.js.\n\nHowever, if you're starting a new project or facing challenges with your current setup, we strongly recommend using Better Auth. Our roadmap includes features previously exclusive to Auth.js, and we hope this will unite the ecosystem more strongly without causing fragmentation.\n\n## 1. Create Better Auth Instance\n\nBefore starting the migration process, set up Better Auth in your project. Follow the [installation guide](https://www.better-auth.com/docs/installation) to get started.\n\nFor example, if you use the GitHub OAuth provider, here is a comparison of the `auth.ts` file.\n\n<Tabs items={[\"Auth.js\", \"Better Auth\"]}>\n<Tabs.Tab>\n```ts \nimport NextAuth from \"next-auth\"\nimport GitHub from \"next-auth/providers/github\"\n  \nexport const { handlers, signIn, signOut, auth } = NextAuth({\n  providers: [GitHub],\n})\n```\n</Tabs.Tab>\n\n<Tabs.Tab>\n```ts\nimport { betterAuth } from \"better-auth\";\n\nexport const auth = betterAuth({\n  socialProviders: { \n    github: { \n      clientId: process.env.GITHUB_CLIENT_ID!, \n      clientSecret: process.env.GITHUB_CLIENT_SECRET!, \n    }, \n  }, \n})\n```\n</Tabs.Tab>\n</Tabs>\n\n<Callout type=\"info\">\n  Now Better Auth supports stateless session management without any database. If\n  you were using a Database adapter in Auth.js, you can refer to the [Database\n  models](#6-database-models) below to check the differences with Better Auth's\n  core schema.\n</Callout>\n\n## 2. Create Client Instance\n\nThis client instance includes a set of functions for interacting with the Better Auth server instance. For more information, see [here](https://www.better-auth.com/docs/concepts/client).\n\n```ts filename=\"auth-client.ts\"\nimport { createAuthClient } from \"better-auth/react\"\n\nexport const authClient = createAuthClient()\n```\n\n## 3. Update the Route Handler\n\nRename the `/app/api/auth/[...nextauth]` folder to `/app/api/auth/[...all]` to avoid confusion. Then, update the `route.ts` file as follows:\n\n<Tabs items={[\"Auth.js\", \"Better Auth\"]}>\n<Tabs.Tab>\n```ts \nimport { handlers } from \"@/lib/auth\"\n\nexport const { GET, POST } = handlers\n```\n</Tabs.Tab>\n\n<Tabs.Tab>\n```ts\nimport { auth } from \"@/lib/auth\";\nimport { toNextJsHandler } from \"better-auth/next-js\";\n\nexport const { POST, GET } = toNextJsHandler(auth)\n```\n</Tabs.Tab>\n</Tabs>\n\n## 4. Session Management\n\nIn this section, we'll look at how to manage sessions in Better Auth compared to Auth.js. For more information, see [here](https://www.better-auth.com/docs/concepts/session-management).\n\n### Client-side\n\n#### Sign In\n\nHere are the differences between Auth.js and Better Auth for signing in users. For example, with the GitHub OAuth provider:\n\n<Tabs items={[\"Auth.js\", \"Better Auth\"]}>\n<Tabs.Tab>\n```ts \n\"use client\"\n\nimport { signIn } from \"next-auth/react\"\n\nsignIn(\"github\")\n\n````\n</Tabs.Tab>\n\n<Tabs.Tab>\n```ts\n\"use client\"\n\nimport { authClient } from \"@/lib/auth-client\";\n\nconst { data, error } = await authClient.signIn.social({\n  provider: \"github\",\n})\n````\n\n</Tabs.Tab>\n</Tabs>\n\n#### Sign Out\n\nHere are the differences between Auth.js and Better Auth when signing out users.\n\n<Tabs items={[\"Auth.js\", \"Better Auth\"]}>\n<Tabs.Tab>\n```ts \n\"use client\"\n\nimport { signOut } from \"next-auth/react\"\n\nsignOut()\n\n````\n</Tabs.Tab>\n\n<Tabs.Tab>\n```ts\n\"use client\"\n\nimport { authClient } from \"@/lib/auth-client\";\n\nconst { data, error } = await authClient.signOut()\n````\n\n</Tabs.Tab>\n</Tabs>\n\n#### Get Session\n\nHere are the differences between Auth.js and Better Auth for getting the current active session.\n\n<Tabs items={[\"Auth.js\", \"Better Auth\"]}>\n<Tabs.Tab>\n```ts \n\"use client\"\n\nimport { useSession } from \"next-auth/react\"\n\nconst { data, status, update } = useSession()\n\n````\n</Tabs.Tab>\n\n<Tabs.Tab>\n```ts\n\"use client\"\n\nimport { authClient } from \"@/lib/auth-client\";\n\nconst { data, error, refetch, isPending, isRefetching } = authClient.useSession()\n````\n\n</Tabs.Tab>\n</Tabs>\n\n### Server-side\n\n#### Sign In\n\nHere are the differences between Auth.js and Better Auth for signing in users. For example, with the GitHub OAuth provider:\n\n<Tabs items={[\"Auth.js\", \"Better Auth\"]}>\n<Tabs.Tab>\n```ts \nimport { signIn } from \"@/lib/auth\"\n\nawait signIn(\"github\")\n\n````\n</Tabs.Tab>\n\n<Tabs.Tab>\n```ts\nimport { auth } from \"@/lib/auth\";\n\nconst { redirect, url } = await auth.api.signInSocial({\n  body: {\n    provider: \"github\",\n  },\n})\n````\n\n</Tabs.Tab>\n</Tabs>\n\n#### Sign Out\n\nHere are the differences between Auth.js and Better Auth when signing out users.\n\n<Tabs items={[\"Auth.js\", \"Better Auth\"]}>\n<Tabs.Tab>\n```ts \nimport { signOut } from \"@/lib/auth\"\n\nawait signOut()\n\n````\n</Tabs.Tab>\n\n<Tabs.Tab>\n```ts\nimport { auth } from \"@/lib/auth\";\nimport { headers } from \"next/headers\";\n\nconst { success } = await auth.api.signOut({\n  headers: await headers(),\n})\n````\n\n</Tabs.Tab>\n</Tabs>\n\n#### Get Session\n\nHere are the differences between Auth.js and Better Auth for getting the current active session.\n\n<Tabs items={[\"Auth.js\", \"Better Auth\"]}>\n<Tabs.Tab>\n```ts \nimport { auth } from \"@/lib/auth\";\n\nconst session = await auth()\n\n````\n</Tabs.Tab>\n\n<Tabs.Tab>\n```ts\nimport { auth } from \"@/lib/auth\";\nimport { headers } from \"next/headers\";\n\nconst session = await auth.api.getSession({\n  headers: await headers(),\n})\n````\n\n</Tabs.Tab>\n</Tabs>\n\n## 5. Protecting Resources\n\n> Proxy (Middleware) is not intended for slow data fetching. While Proxy can be helpful for optimistic checks such as permission-based redirects, it should not be used as a full session management or authorization solution. - [Next.js docs](https://nextjs.org/docs/app/getting-started/proxy#use-cases)\n\nAuth.js offers approaches using Proxy (Middleware), but Better Auth recommend handling auth checks on each page or route rather than in a Proxy or Layout. Here is a basic example of protecting resources with Better Auth.\n\n<Tabs items={[\"Client-side\", \"Server-side\"]}>\n<Tabs.Tab>\n```ts filename=\"app/dashboard/page.tsx\"\n\"use client\";\n\nimport { authClient } from \"@/lib/auth-client\"\nimport { redirect } from \"next/navigation\"\n\nconst DashboardPage = () => {\nconst { data, error, isPending } = authClient.useSession();\n\nif (isPending) {\nreturn <div>Pending</div>;\n}\nif (!data || error) {\nredirect(\"/sign-in\");\n}\n\nreturn (\n\n<div>\n  <h1>Welcome {data.user.name}</h1>\n</div>\n); };\n\nexport default DashboardPage;\n```\n</Tabs.Tab>\n\n<Tabs.Tab>\n```ts filename=\"app/dashboard/page.tsx\"\nimport { auth } from \"@/lib/auth\";\nimport { headers } from \"next/headers\";\nimport { redirect } from \"next/navigation\";\n\nconst DashboardPage = async () => {\nconst session = await auth.api.getSession({\nheaders: await headers(),\n});\n\nif (!session) {\nredirect(\"/sign-in\");\n}\n\nreturn (\n\n<div>\n  <h1>Welcome {session.user.name}</h1>\n</div>\n); };\n\nexport default DashboardPage\n```\n</Tabs.Tab>\n</Tabs>\n\n## 6. Database models\n\nBoth Auth.js and Better Auth provide stateless (JWT) and database session strategies. If you were using the database session strategy in Auth.js and plan to continue using it in Better Auth, you will also need to migrate your database.\n\nJust like Auth.js has database models, Better Auth also has a core schema. In this section, we'll compare the two and explore the differences between them.\n\n<Tabs items={[\"Auth.js\", \"Better Auth\"]}>\n<Tabs.Tab>\n```mermaid\n  erDiagram\n    User ||--|{ Account : \"\"\n    User {\n      string id\n      string name\n      string email\n      timestamp emailVerified\n      string image\n    }\n    User ||--|{ Session : \"\"\n    Session {\n      string id\n      timestamp expires\n      string sessionToken\n      string userId\n    }\n    Account {\n      string id\n      string userId\n      string type\n      string provider\n      string providerAccountId\n      string refresh_token\n      string access_token\n      int expires_at\n      string token_type\n      string scope\n      string id_token\n      string session_state\n    }\n    User ||--|{ VerificationToken : \"\"\n    VerificationToken {\n      string identifier\n      string token\n      timestamp expires\n    }\n```\n</Tabs.Tab>\n<Tabs.Tab>\n```mermaid\nerDiagram\n    User ||--|{ Account : \"\"\n    User {\n      string id\n      string name\n      string email\n      boolean emailVerified\n      string image\n      timestamp createdAt\n      timestamp updatedAt\n    }\n    User ||--|{ Session : \"\"\n    Session {\n      string id\n      string userId\n      string token\n      timestamp expiresAt\n      string ipAddress\n      string userAgent\n      timestamp createdAt\n      timestamp updatedAt\n    }\n    Account {\n      string id\n      string userId\n      string accountId\n      string providerId\n      string accessToken\n      string refreshToken\n      timestamp accessTokenExpiresAt\n      timestamp refreshTokenExpiresAt\n      string scope\n      string idToken\n      string password\n      timestamp createdAt\n      timestamp updatedAt\n    }\n    User ||--|{ Verification : \"\"\n    Verification {\n      string id\n      string identifier\n      string value\n      timestamp expiresAt\n      timestamp createdAt\n      timestamp updatedAt\n    }\n```\n</Tabs.Tab>\n</Tabs>\n\n### Comparison\n\nTable: <strong className='underline italic'>User</strong>\n\n- `name`, `email`, and `emailVerified` are required in Better Auth, while optional in Auth.js\n- `emailVerified` uses a boolean in Better Auth, while Auth.js uses a timestamp\n- Better Auth includes `createdAt` and `updatedAt` timestamps\n\nTable: <strong className='underline italic'>Session</strong>\n\n- Better Auth uses `token` instead of `sessionToken`\n- Better Auth uses `expiresAt` instead of `expires`\n- Better Auth includes `ipAddress` and `userAgent` fields\n- Better Auth includes `createdAt` and `updatedAt` timestamps\n\nTable: <strong className='underline italic'>Account</strong>\n\n- Better Auth uses camelCase naming (e.g. `refreshToken` vs `refresh_token`)\n- Better Auth includes `accountId` to distinguish between the account ID and internal ID\n- Better Auth uses `providerId` instead of `provider`\n- Better Auth includes `accessTokenExpiresAt` and `refreshTokenExpiresAt` for token management\n- Better Auth includes `password` field to support built-in credential authentication\n- Better Auth does not have a `type` field as it's determined by the `providerId`\n- Better Auth removes `token_type` and `session_state` fields\n- Better Auth includes `createdAt` and `updatedAt` timestamps\n\nTable: <strong className='underline italic'>VerificationToken -> Verification</strong>\n\n- Better Auth uses `Verification` table instead of `VerificationToken`\n- Better Auth uses a single `id` primary key instead of composite primary key\n- Better Auth uses `value` instead of `token` to support various verification types\n- Better Auth uses `expiresAt` instead of `expires`\n- Better Auth includes `createdAt` and `updatedAt` timestamps\n\n<Callout type=\"info\">\n  If you were using Auth.js v4, note that v5 does not introduce any breaking\n  changes to the database schema. Optional fields like `oauth_token_secret` and\n  `oauth_token` can be removed if you are not using them. Rarely used fields\n  like `refresh_token_expires_in` can also be removed.\n</Callout>\n\n### Customization\n\nYou may have extended the database models or implemented additional logic in Auth.js. Better Auth allows you to customize the core schema in a type-safe way. You can also define custom logic during the lifecycle of database operations. For more details, see [Concepts - Database](https://www.better-auth.com/docs/concepts/database).\n\n## Wrapping Up\n\nNow you're ready to migrate from Auth.js to Better Auth. For a complete implementation with multiple authentication methods, check out the [Next.js Demo App](https://github.com/better-auth/better-auth/tree/canary/demo/nextjs). Better Auth offers greater flexibility and more features, so be sure to explore the [documentation](https://www.better-auth.com/docs) to unlock its full potential.\n\nIf you need help with migration, join our [community](https://www.better-auth.com/community) or reach out to [contact@better-auth.com](mailto:contact@better-auth.com).\n"
  },
  {
    "path": "docs/pages/getting-started/migrating-to-v5.mdx",
    "content": "---\ntitle: Migrating to v5\n---\n\nimport { Callout, Tabs, Steps } from \"nextra/components\"\n\n# Upgrade Guide (NextAuth.js v5)\n\n<Callout type=\"info\">\n  This guide only applies to `next-auth` upgrades for users of Next.js. Feel\n  free to skip to the next section,\n  [Installation](/getting-started/installation), if you are not upgrading to\n  `next-auth@5`.\n</Callout>\n\n**NextAuth.js version 5** is a major rewrite of the `next-auth` package, that being said, we introduced as few breaking changes as possible. For all else, this document will guide you through the migration process.\n\nGet started by installing the latest version of `next-auth` with the `beta` tag.\n\n```bash npm2yarn\nnpm install next-auth@beta\n```\n\n## New Features\n\n#### Main changes\n\n- App Router-first (`pages/` still supported)\n- OAuth support on preview deployments ([Read more](/getting-started/deployment#securing-a-preview-deployment))\n- Simplified setup (shared config, inferred [env variables](/guides/environment-variables#environment-variable-inference))\n- New `account()` callback on providers ([`account()` docs](/reference/core/providers#account))\n- Edge-compatible\n\n#### Universal `auth()`\n\n- A single method to authenticate anywhere\n- Use `auth()` instead of `getServerSession`, `getSession`, `withAuth`, `getToken`, and `useSession` ([Read more](#authenticating-server-side))\n\n## Breaking Changes\n\n- Auth.js now builds on `@auth/core` with stricter [OAuth](https://www.ietf.org/rfc/rfc6749.html)/[OIDC](https://openid.net/specs/openid-connect-core-1_0.html) spec-compliance, which might break some existing OAuth providers. See our [open issues](https://github.com/nextauthjs/next-auth/issues?q=is%3Aopen+is%3Aissue+label%3Aproviders) for more details.\n- OAuth 1.0 support is deprecated.\n- The minimum required Next.js version is now [14.0](https://nextjs.org/14).\n- The import `next-auth/next` is replaced. See [Authenticating server-side](#authenticating-server-side) for more details.\n- The import `next-auth/middleware` is replaced. See [Authenticating server-side](#authenticating-server-side) for more details. As of Next.js 16, `middleware.ts` has been renamed to `proxy.ts`.\n- When the `idToken: boolean` option is set to `false`, it won't entirely disable the ID token. Instead, it signals `next-auth` to also visit the `userinfo_endpoint` for the final user data. Previously, `idToken: false` opted out to check the `id_token` validity at all.\n\n## Migration\n\n<Steps>\n\n### Configuration File\n\nOne of our goals was to avoid exporting your configuration from one file and passing it around as `authOptions` throughout your application. To achieve this, we settled on moving the configuration file to the root of the repository and having it export the necessary functions you can use everywhere else. (`auth`, `signIn`, `signOut`, `handlers` etc.).\n\nThe configuration file should look very similar to your previous route-based Auth.js configuration. Except that we're doing it in a new file in the root of the repository now, and we're exporting methods to be used elsewhere. Below is a simple example of a v5 configuration file.\n\n```ts filename=\"./auth.ts\" {5}\nimport NextAuth from \"next-auth\"\nimport GitHub from \"next-auth/providers/github\"\nimport Google from \"next-auth/providers/google\"\n\nexport const { auth, handlers, signIn, signOut } = NextAuth({\n  providers: [GitHub, Google],\n})\n```\n\nSome things to note about the new configuration:\n\n1. This is now in a file named `auth.ts` in the root of your repository. It can technically be named anything, but you'll be importing the exported methods from here across your app, so we'd recommend keeping it short.\n2. There is no need to install `@auth/core` to import the provider definitions from, these come from `next-auth` itself.\n3. The configuration object passed to the `NextAuth()` function is the same as before.\n4. The returned methods exported from the `NextAuth()` function call are new and will be required elsewhere in your application.\n\nThe old configuration file, contained in the API Route (`pages/api/auth/[...nextauth].ts` / `app/api/auth/[...nextauth]/route.ts`), now becomes much shorter. **These exports are designed to be used in an App Router API Route**, but the rest of your app can stay in the Pages Router if you are gradually migrating!\n\n```ts filename=\"app/api/auth/[...nextauth]/route.ts\"\nimport { handlers } from \"@/auth\"\nexport const { GET, POST } = handlers\n```\n\n## Authenticating server-side\n\nAuth.js has had a few different ways to authenticate server-side in the past, so we've tried to simplify this as much as possible.\n\nNow that Next.js components are **server-first** by default, and thanks to the investment in using standard Web APIs, we were able to simplify the authentication process to a single `auth()` function call in most cases.\n\n### Authentication methods\n\nSee the table below for a summary of the changes. Below that are `diff` examples of how to use the new `auth()` method in various environments and contexts.\n\n| Where                   | v4                                                    | v5                               |\n| ----------------------- | ----------------------------------------------------- | -------------------------------- |\n| **Server Component**    | `getServerSession(authOptions)`                       | `auth()` call                    |\n| **Proxy (Middleware)**  | `withAuth(middleware, subset of authOptions)` wrapper | `auth` export / `auth()` wrapper |\n| **Client Component**    | `useSession()` hook                                   | `useSession()` hook              |\n| **Route Handler**       | _Previously not supported_                            | `auth()` wrapper                 |\n| **API Route (Edge)**    | _Previously not supported_                            | `auth()` wrapper                 |\n| **API Route (Node.js)** | `getServerSession(req, res, authOptions)`             | `auth(req, res)` call            |\n| **API Route (Node.js)** | `getToken(req)` (No session rotation)                 | `auth(req, res)` call            |\n| **getServerSideProps**  | `getServerSession(ctx.req, ctx.res, authOptions)`     | `auth(ctx)` call                 |\n| **getServerSideProps**  | `getToken(ctx.req)` (No session rotation)             | `auth(req, res)` call            |\n\n#### Details\n\n<Tabs items={[\"Server Component (App)\", \"Client Component (App)\", \"Middleware\", \"API Routes (Pages)\", \"getServerSideProps (Pages)\"]}>\n\n<Tabs.Tab>\n\nAuth.js v4 has supported reading the session in Server Components for a while via `getServerSession`. This has been also simplified to the same `auth()` function.\n\n```diff filename=\"app/page.tsx\"\n- import { authOptions } from \"pages/api/auth/[...nextauth]\"\n- import { getServerSession } from \"next-auth/next\"\n+ import { auth } from \"@/auth\"\n\nexport default async function Page() {\n-  const session = await getServerSession(authOptions)\n+  const session = await auth()\n  return (<p>Welcome {session?.user.name}!</p>)\n}\n```\n\n</Tabs.Tab>\n<Tabs.Tab>\n\nImports from `next-auth/react` are now marked with the [`\"use client\"`](https://nextjs.org/docs/getting-started/react-essentials#the-use-client-directive) directive. Therefore, they can be used in client components just like they were used in previous versions. Don't forget, client components that attempt to access the session via context will need to be wrapped in a `<SessionProvider />`.\n\n```ts filename=\"components/clientComponent.tsx\"\n'use client';\n\nimport { useSession, SessionProvider } from 'next-auth/react';\n\nconst ClientComponent = () => {\n  const session = useSession();\n\n  return (\n    <SessionProvider>\n      <p>Welcome {session?.user?.name}</p>\n    </SessionProvider>\n  )\n}\n```\n\n</Tabs.Tab>\n<Tabs.Tab>\n\n```diff filename=\"proxy.ts (middleware.ts in Next.js < 16)\"\n- export { default } from \"next-auth/middleware\"\n+ export { auth as proxy } from \"@/auth\"\n```\n\nFor advanced use cases, you can use `auth` as a wrapper for your Proxy:\n\n```ts filename=\"proxy.ts\"\nimport { auth } from \"@/auth\"\n\nexport const proxy = auth((req) => {\n  // req.auth\n})\n\n// Optionally, don't invoke Proxy on some paths\nexport const config = {\n  matcher: [\"/((?!api|_next/static|_next/image|favicon.ico).*)\"],\n}\n```\n\nCheck out additional [Proxy docs](/getting-started/session-management/protecting#nextjs-proxy) for more details.\n\n</Tabs.Tab>\n<Tabs.Tab>\n\nInstead of importing `getServerSession` from `next-auth/next` or `getToken` from `next-auth/jwt`, you can now import the `auth` function exported from your `auth.ts` config file and call it without passing `authOptions`.\n\n```diff filename='pages/api/example.ts'\n- import { getServerSession } from \"next-auth/next\"\n- import { getToken } from \"next-auth/jwt\"\n- import { authOptions } from \"pages/api/auth/[...nextauth]\"\n+ import { auth } from \"@/auth\"\n+ import { NextApiRequest, NextApiResponse } from \"next\"\n\nexport default async function handler(req: NextApiRequest, res: NextApiResponse) {\n-  const session = await getServerSession(req, res, authOptions)\n-  const token = await getToken({ req })\n+  const session = await auth(req, res)\n  if (session) return res.json(\"Success\")\n  return res.status(401).json(\"You must be logged in.\");\n}\n```\n\n<Callout>\n  Whenever `auth()` is passed the res object, it will rotate the session expiry.\n  This was not the case with `getToken()` previously. The default session expiry\n  is 30 days, but you can change it by setting `authOptions.session.maxAge`.\n</Callout>\n\n</Tabs.Tab>\n<Tabs.Tab>\n\nInstead of importing `getServerSession` from `next-auth/next` or `getToken` from `next-auth/jwt`, you can now import the `auth` function from your config file and call it without passing `authOptions`.\n\n```diff filename=\"pages/protected.tsx\"\n- import { getServerSession } from \"next-auth/next\"\n- import { getToken } from \"next-auth/jwt\"\n- import { authOptions } from \"pages/api/auth/[...nextauth]\"\n+ import { auth } from \"@/auth\"\n\nexport const getServerSideProps: GetServerSideProps = async (context) => {\n-  const session = await getServerSession(context.req, context.res, authOptions)\n-  const token = await getToken({ req: context.req })\n+  const session = await auth(context)\n  if (session) {\n    // Do something with the session\n  }\n\n  return { props: { session } }\n}\n```\n\n<Callout>\n  Whenever `auth()` is passed the `res` object (i.e. as a part of `context`), it\n  will rotate the session expiry. This was not the case with `getToken()`\n  previously.\n</Callout>\n\n</Tabs.Tab>\n</Tabs>\n\n## Adapters\n\n### Adapter packages\n\nBeginning with `next-auth` v5, you should now install database adapters from the `@auth/*-adapter` scope, instead of `@next-auth/*-adapter`. Database adapters don't rely on any Next.js features, so it made more sense to move them to this new scope.\n\n```diff\n- npm install @next-auth/prisma-adapter\n+ npm install @auth/prisma-adapter\n```\n\n<Callout type=\"info\">\n  Check out the [adapters page](/getting-started/database) for a list of\n  official adapters, or the \"[create a database\n  adapter](/guides/creating-a-database-adapter)\" guide to learn how to create\n  your own.\n</Callout>\n\n### Database Migrations\n\nNextAuth.js v5 **does not introduce any breaking changes to the database schema**. However, since OAuth 1.0 support is dropped, the (previously optional) `oauth_token_secret` and `oauth_token` fields can be removed from the `account` table if you are not using them.\n\nFurthermore, previously uncommon fields like GitHub's `refresh_token_expires_in` fields were\nrequired to be added to the `account` table. This is no longer the case, and you can remove it if\nyou are not using it. If you do use it, please make sure to return it via the new [`account()` callback](/reference/core/providers#account).\n\n## Edge compatibility\n\nWhile Auth.js strictly uses standard [Web APIs](https://developer.mozilla.org/en-US/docs/Web/API) (and thus can run in any environment that supports them), some libraries or ORMs (Object-Relational Mapping) packages that you rely on might not be ready yet. In this case, you can split the auth configuration into multiple files.\n\nAuth.js supports two [session strategies](/concepts/session-strategies). When you are using an adapter, it will default to the `database` strategy. **Unless your database and its adapter is compatible with the Edge runtime/infrastructure, you will not be able to use the `\"database\"` session strategy**.\n\nSo for example, if you are using an adapter that relies on an ORM/library that is not yet compatible with Edge runtime(s) below is an example where we force the `jwt` strategy and split up the configuration so the library doesn't attempt to access the database in edge environments, like in the proxy (or middleware for older Next.js versions).\n\n<Callout>\n  The following filenames are only a convention, they can be named anything you\n  like.\n</Callout>\n\n1. Create an `auth.config.ts` file which exports an object containing your Auth.js configuration options. You can put all common configuration here **which does not rely on the adapter**. Notice this is exporting a configuration object only, we're not calling `NextAuth()` here.\n\n```ts filename=\"auth.config.ts\"\nimport GitHub from \"next-auth/providers/github\"\nimport type { NextAuthConfig } from \"next-auth\"\n\nexport default { providers: [GitHub] } satisfies NextAuthConfig\n```\n\n2. Next, create an `auth.ts` file and add your adapter and the `jwt` session strategy there. This is the `auth.ts` configuration file you will import from in the rest of your application, other than in the middleware.\n\n```ts filename=\"auth.ts\" {4, 9, 11}\nimport NextAuth from \"next-auth\"\nimport { PrismaAdapter } from \"@auth/prisma-adapter\"\nimport { PrismaClient } from \"@prisma/client\"\nimport authConfig from \"./auth.config\"\n\nconst prisma = new PrismaClient()\n\nexport const { auth, handlers, signIn, signOut } = NextAuth({\n  adapter: PrismaAdapter(prisma),\n  session: { strategy: \"jwt\" },\n  ...authConfig,\n})\n```\n\n3. In your proxy file (or middleware file for older Next.js versions), import the configuration object from your first `auth.config.ts` file and use it to lazily initialize Auth.js there. In effect, initialize Auth.js separately with all of your common options, but **without the edge incompatible adapter**.\n\n```ts filename=\"proxy.ts\" {1} /NextAuth/\nimport authConfig from \"./auth.config\"\nimport NextAuth from \"next-auth\"\n\n// Use only one of the two proxy options below\n// 1. Use proxy directly\n// export const { auth: proxy } = NextAuth(authConfig)\n\n// 2. Wrapped proxy option\nconst { auth } = NextAuth(authConfig)\nexport const proxy = auth(async function proxy(req: NextRequest) {\n  // Your custom proxy logic goes here\n})\n```\n\nThe above is just an example. **The main idea**, is to separate the part of the configuration that is edge-compatible from the rest, and only import the edge-compatible part in Proxy/Edge pages/routes. You can read more about this workaround in the [Prisma docs](/getting-started/adapters/prisma), for example.\n\nPlease follow up with your library/database/ORM's maintainer to see if they are planning to support the Edge runtime/infrastructure.\n\nFor more information in general about edge compatibility and how Auth.js fits into this, check out our [edge compatibility article](/guides/edge-compatibility).\n\n## Environment Variables\n\nThere are **no breaking changes to the environment variables**, but we have cleaned up a few things which make some of them unnecessary. Therefore, we wanted to share some best practices around environment variables.\n\n- All environment variables should be prefixed with `AUTH_`, `NEXTAUTH_` is no longer in use.\n- If you name your provider `secret` / `clientId` variables using this syntax, i.e. `AUTH_GITHUB_SECRET` and `AUTH_GITHUB_ID`, they will be auto-detected and you won't have to explicitly pass them into your provider's configuration.\n- The `NEXTAUTH_URL`/`AUTH_URL` is not strictly necessary anymore in most environments. We will auto-detect the host based on the request headers.\n- The `AUTH_TRUST_HOST` environment variable serves the same purpose as setting `trustHost: true` in your Auth.js configuration. This is necessary when running Auth.js behind a proxy. When set to true we will trust the `X-Forwarded-Host` and `X-Forwarded-Proto` headers passed to the app by the proxy to auto-detect the host URL (`AUTH_URL`)\n- The `AUTH_SECRET` environment variable is the **only variable that is really necessary**. You do not need to additionally pass this value into your config as the `secret` configuration option if you've set the environment variable.\n\nFor more information about environment variables and environment variable inference, check out our [environment variable](/guides/environment-variables) page.\n\n## TypeScript\n\n- `NextAuthOptions` is renamed to `NextAuthConfig`\n- The following types are now exported from all framework packages like `next-auth` and `@auth/sveltekit`:\n\n```ts\nexport type {\n  Account,\n  DefaultSession,\n  Profile,\n  Session,\n  User,\n} from \"@auth/core/types\"\n```\n\n- All `Adapter` types are re-exported from `/adapters` in the framework packages as well, i.e. from `next-auth/adapters`, `@auth/sveltekit/adapters`, etc.\n\n</Steps>\n\n## Cookies\n\n- The `next-auth` prefix is renamed to `authjs`.\n\n## Summary\n\nWe hope this migration goes smoothly for everyone! If you have any questions or get stuck anywhere, feel free to create [a new issue](https://github.com/nextauthjs/next-auth/issues/new) on GitHub, or come chat with us in the [Discord](https://discord.authjs.dev) server.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/42-school.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/42-school.svg\" height=\"64\" width=\"64\" />\n\n# 42School Provider\n\n## Resources\n\n- [42School OAuth documentation](https://api.intra.42.fr/apidoc/guides/web_application_flow)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/42-school\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/42-school\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/42-school\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_42_SCHOOL_ID\nAUTH_42_SCHOOL_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport 42School from \"next-auth/providers/42-school\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [42School],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport 42School from \"@auth/qwik/providers/42-school\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [42School],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport 42School from \"@auth/sveltekit/providers/42-school\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [42School],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport 42School from \"@auth/express/providers/42-school\"\n\napp.use(\"/auth/*\",\n  ExpressAuth({ providers: [ 42School ] })\n)\n```\n\n  </Code.Express>\n</Code>\n\n## Notes\n\n- 42 returns a field on `Account` called `created_at` which is a number, this is different from the default schema's datatype for this field. Check out the [42 School docs](https://api.intra.42.fr/apidoc/guides/getting_started#make-basic-requests) for more info. Make sure to add or edit this field in your database schema in case if you are using an [database adapter](/getting-started/database).\n"
  },
  {
    "path": "docs/pages/getting-started/providers/apple.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/apple.svg\" width=\"64\" height=\"65\" />\n\n# Apple Provider\n\n## Resources\n\n- Sign in with Apple [Overview](https://developer.apple.com/sign-in-with-apple/get-started/)\n- Sign in with Apple [REST API](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api)\n- [How to retrieve](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple#3383773) the user's information from Apple ID servers\n\n> **_NOTE:_** Apple currently does not support [RedirectProxyUrl](https://github.com/nextauthjs/next-auth/blob/3ec06842682a31e53fceabca701a362abda1e7dd/packages/core/src/lib/utils/providers.ts#L48) usage.\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/apple\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/apple\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/apple\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_APPLE_ID\nAUTH_APPLE_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Apple from \"next-auth/providers/apple\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Apple],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Apple from \"@auth/qwik/providers/apple\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Apple],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Apple from \"@auth/sveltekit/providers/apple\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Apple],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Apple from \"@auth/express/providers/apple\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Apple] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/asgardeo.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/asgardeo.svg\" width=\"64\" height=\"64\" />\n\n# Asgardeo Provider\n\n## Resources\n\n- [Asgardeo - Authentication Guide](https://wso2.com/asgardeo/docs/guides/authentication)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/asgardeo\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/asgardeo\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/asgardeo\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_ASGARDEO_ID\nAUTH_ASGARDEO_SECRET\nAUTH_ASGARDEO_ISSUER\n```\n\n### Configuration\n\nFollow these steps:\n\n1. Log into the [Asgardeo console](https://console.asgardeo.io)\n2. Next, go to \"Application\" tab (more info [here](https://wso2.com/asgardeo/docs/guides/applications/register-oidc-web-app/))\n3. Register a standard based, Open ID connect, application\n4. Add the **callback URLs**: `http://localhost:3000/api/auth/callback/asgardeo` (development) and `https://{YOUR_DOMAIN}.com/api/auth/callback/asgardeo` (production)\n5. After registering the application, go to \"Protocol\" tab.\n6. Check `code` as the grant type.\n7. Add \"Authorized redirect URLs\" & \"Allowed origins fields\"\n8. Make Email, First Name, Photo URL user attributes mandatory from the console.\n\nThen, add the ClientID, ClientSecret, and Issuer values to your environment variables.\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Asgardeo from \"next-auth/providers/asgardeo\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Asgardeo],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Asgardeo from \"@auth/qwik/providers/asgardeo\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Asgardeo],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Asgardeo from \"@auth/sveltekit/providers/asgardeo\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Asgardeo],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Asgardeo from \"@auth/express/providers/asgardeo\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Asgardeo] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/auth0.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/auth0.svg\" width=\"64\" height=\"64\" />\n\n# Auth0 Provider\n\n## Resources\n\n- [Auth0 documentation](https://auth0.com/docs/authenticate)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/auth0\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/auth0\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/auth0\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_AUTH0_ID\nAUTH_AUTH0_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Auth0 from \"next-auth/providers/auth0\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Auth0],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Auth0 from \"@auth/qwik/providers/auth0\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Auth0],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Auth0 from \"@auth/sveltekit/providers/auth0\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Auth0],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Auth0 from \"@auth/express/providers/auth0\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Auth0] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/authentik.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/authentik.svg\" height=\"64\" width=\"64\" />\n\n# Authentik Provider\n\n## Resources\n\n- [Authentik OAuth documentation](https://goauthentik.io/docs/providers/oauth2)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/authentik\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/authentik\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/authentik\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_AUTHENTIK_ID\nAUTH_AUTHENTIK_SECRET\nAUTH_AUTHENTIK_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\";\nimport Authentik from \"next-auth/providers/authentik\";\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Authentik({\n    clientId: AUTH_AUTHENTIK_CLIENT_ID\n    clientSecret: AUTH_AUTHENTIK_CLIENT_SECRET\n    issuer: AUTH_AUTHENTIK_ISSUER\n  })]\n});\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Authentik from \"@auth/qwik/providers/authentik\";\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Authentik({\n        clientId: import.meta.env.AUTH_AUTHENTIK_CLIENT_ID\n        clientSecret: import.meta.env.AUTH_AUTHENTIK_CLIENT_SECRET\n        issuer: import.meta.env.AUTH_AUTHENTIK_ISSUER\n      })\n    ],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\";\nimport Authentik from \"@auth/sveltekit/providers/authentik\";\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Authentik({\n    clientId: AUTH_AUTHENTIK_CLIENT_ID\n    clientSecret: AUTH_AUTHENTIK_CLIENT_SECRET\n    issuer: AUTH_AUTHENTIK_ISSUER\n  })]\n});\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\";\nimport Authentik from \"@auth/express/providers/authentik\";\n\napp.use(\"/auth/*\", ExpressAuth({\n  providers: [Authentik({\n    clientId: AUTH_AUTHENTIK_CLIENT_ID\n    clientSecret: AUTH_AUTHENTIK_CLIENT_SECRET\n    issuer: AUTH_AUTHENTIK_ISSUER\n  })]\n}));\n```\n\n  </Code.Express>\n</Code>\n\n<Callout>\n  issuer should include the slug without a trailing slash – e.g.,\n  https://my-authentik-domain.com/application/o/My_Slug\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/azure-ad-b2c.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/azure.svg\" height=\"64\" width=\"64\" />\n\n# Azure AD B2C Provider\n\n## Resources\n\n- [Azure Active Directory B2C documentation](https://learn.microsoft.com/en-us/azure/active-directory-b2c)\n- [What is Azure AD B2C](https://learn.microsoft.com/en-us/azure/active-directory-b2c/overview)\n- [Azure AD B2C Tenant](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant)\n- [App Registration](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-register-applications)\n- [User Flow](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows)\n\n## Setup\n\n### Environment Variables\n\n```\nAUTH_AZURE_AD_B2C_ID\nAUTH_AZURE_AD_B2C_SECRET\nAUTH_AZURE_AD_B2C_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\";\nimport AzureADB2C from \"next-auth/providers/azure-ad-b2c\";\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [AzureADB2C({\n    clientId: AUTH_AZURE_AD_B2C_CLIENT_ID\n    clientSecret: AUTH_AZURE_AD_B2C_CLIENT_SECRET\n    issuer: AUTH_AZURE_AD_B2C_ISSUER\n  })]\n});\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport AzureADB2C from \"@auth/qwik/providers/azure-ad-b2c\";\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      AzureADB2C({\n        clientId: import.meta.env.AUTH_AZURE_AD_CLIENT_ID\n        clientSecret: import.meta.env.AUTH_AZURE_AD_CLIENT_SECRET\n        issuer: import.meta.env.AUTH_AZURE_AD_ISSUER\n      })\n    ],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\";\nimport AzureADB2C from \"@auth/sveltekit/providers/azure-ad-b2c\";\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [AzureADB2C({\n    clientId: AUTH_AZURE_AD_CLIENT_ID\n    clientSecret: AUTH_AZURE_AD_CLIENT_SECRET\n    issuer: AUTH_AZURE_AD_ISSUER\n  })]\n});\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\";\nimport AzureADB2C from \"@auth/express/providers/azure-ad-b2c\";\n\napp.use(\"/auth/*\", ExpressAuth({\n  providers: [AzureADB2C({\n    clientId: AUTH_AZURE_AD_CLIENT_ID\n    clientSecret: AUTH_AZURE_AD_CLIENT_SECRET\n    issuer: AUTH_AZURE_AD_ISSUER\n  })]\n}));\n```\n\n  </Code.Express>\n</Code>\n\n### Tenant Setup\n\nBasic configuration sets up Azure AD B2C to return an ID Token. This should be done as a prerequisite prior to running through the Advanced configuration. In the Tenant Setup, make sure to set the following during \"User attributes and token claims\".\n\n- Collect attribute:\n  - Email Address\n  - Display Name\n  - Given Name\n  - Surname\n- Return claim:\n  - Email Addresses\n  - Display Name\n  - Given Name\n  - Surname\n  - Identity Provider\n  - Identity Provider Access Token\n  - User's Object ID\n"
  },
  {
    "path": "docs/pages/getting-started/providers/azure-ad.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/azure.svg\" height=\"64\" width=\"64\" />\n\n# Azure AD Provider\n\n<Callout type=\"warning\">\n  Deprecated - Microsoft has rebranded this product [Microsoft Entra\n  ID](/getting-started/providers/microsoft-entra-id) and all support work will\n  be going into that IdP. We recommend you migrate to using that provider as\n  well.\n</Callout>\n\n## Resources\n\n- [AzureAD OAuth documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow/)\n- [AzureAD OAuth apps](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/azure-ad\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/azure-ad\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/azure-ad\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_AZURE_AD_ID\nAUTH_AZURE_AD_SECRET\nAUTH_AZURE_AD_TENANT_ID\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport AzureAd from \"next-auth/providers/azure-ad\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [AzureAd],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport AzureAd from \"@auth/qwik/providers/azure-ad\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [AzureAd],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport AzureAd from \"@auth/sveltekit/providers/azure-ad\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [AzureAd],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport AzureAd from \"@auth/express/providers/azure-ad\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [AzureAd] }))\n```\n\n  </Code.Express>\n</Code>\n\n#### To allow specific Active Directory users access:\n\n- In https://portal.azure.com/ search for \"Azure Active Directory\", and select your organization.\n- Next, go to \"App Registration\" in the left menu, and create a new one.\n- Pay close attention to \"Who can use this application or access this API?\"\n  - This allows you to scope access to specific types of user accounts\n  - Only your tenant, all azure tenants, or all azure tenants and personal Microsoft accounts (Skype, Xbox, Outlook.com, etc.)\n- When asked for a redirection URL, use `https://yourapplication.com/api/auth/callback/azure-ad` or for development `http://localhost:3000/api/auth/callback/azure-ad`.\n- After your App Registration is created, under \"Client Credential\" create your Client secret.\n- Click on \"API Permissions\" and click \"Grant admin consent for...\" to allow User.Read access to your tenant.\n- Now copy your:\n  - Application (client) ID\n  - Directory (tenant) ID\n  - Client secret (value)\n\nIn `.env.local` create the following entries:\n\n```\nAUTH_AZURE_AD_CLIENT_ID=<copy Application (client) ID here>\nAUTH_AZURE_AD_CLIENT_SECRET=<copy generated client secret value here>\nAUTH_AZURE_AD_TENANT_ID=<copy the tenant id here>\n```\n\nThat will default the tenant to use the `common` authorization endpoint. [For more details see here](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#endpoints).\n\nIf you want your application to receive authorization requests from not only the tenants but also all Microsoft users just add \"common\" in AUTH_AZURE_AD_TENANT_ID, this will \"skip\" tenants authorization.\n\n```\nAUTH_AZURE_AD_TENANT_ID=common\n```\n\n<Callout type=\"info\">\n  Azure AD returns the profile picture in an ArrayBuffer, instead of just a URL\n  to the image, so our provider converts it to a base64 encoded image string and\n  returns that instead. See:\n  https://docs.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0#examples.\n  The default image size is 48x48 to avoid [running out of\n  space](https://next-auth.js.org/faq#:~:text=What%20are%20the%20disadvantages%20of%20JSON%20Web%20Tokens%3F)\n  in case the session is saved as a JWT.\n</Callout>\n\nIn `pages/api/auth/[...nextauth].js` find or add the `AzureAD` entries:\n\n```js\nimport AzureADProvider from \"next-auth/providers/azure-ad\"\n\nproviders: [\n  AzureADProvider({\n    clientId: process.env.AZURE_AD_CLIENT_ID,\n    clientSecret: process.env.AZURE_AD_CLIENT_SECRET,\n    tenantId: process.env.AZURE_AD_TENANT_ID,\n  }),\n]\n```\n"
  },
  {
    "path": "docs/pages/getting-started/providers/azure-devops.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img\n  align=\"right\"\n  src=\"/img/providers/azure-devops.svg\"\n  height=\"64\"\n  width=\"64\"\n/>\n\n# Azure DevOps Provider\n\n<Callout type=\"warning\">\n  Deprecated - While still available, Microsoft is [no longer\n  supporting](https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops#available-oauth-models)\n  Azure DevOps OAuth and recommends using [Microsoft Entra\n  ID](/getting-started/providers/microsoft-entra-id) instead.\n</Callout>\n\n## Resources\n\n- [Azure DevOps Apps documentation](https://aex.dev.azure.com)\n- [Microsoft documentation](https://docs.microsoft.com/en-us) · [Azure DevOps](https://docs.microsoft.com/en-us/azure/devops/) · [Authorize access to REST APIs with OAuth 2.0](https://docs.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops])\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/azure-devops\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/azure-devops\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/azure-devops\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment variables\n\nIn `.env.local` create the following entries:\n\n```\nAZURE_DEVOPS_APP_ID=<copy App ID value here>\nAZURE_DEVOPS_CLIENT_SECRET=<copy generated client secret value here>\n```\n\n### Register application\n\n[`https://app.vsaex.visualstudio.com/app/register`](https://app.vsaex.visualstudio.com/app/register)\n\nProvide the required details:\n\n1. Company name\n2. Application name\n3. Application website\n4. Authorization callback URL\n   - `https://example.com/api/auth/callback/azure-devops` for production\n   - `https://localhost/api/auth/callback/azure-devops` for development\n5. Authorized scopes\n   - Required minimum is `User profile (read)`\n\nClick ‘Create Application’\n\n<Callout type=\"warning\">\n- You are required to use HTTPS even for the localhost\n\n- You will have to delete and create a new application to change the scopes later\n\n</Callout>\n\nThe following data is relevant for the next step:\n\n- App ID\n- Client Secret (after clicking the ‘Show’ button, ignore App Secret entry above it)\n- Authorized Scopes\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport AzureDevOps from \"next-auth/providers/azure-devops\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    AzureDevOps({\n      clientId: AUTH_AZURE_DEVOPS_APP_ID,\n      clientSecret: AUTH_AZURE_DEVOPS_CLIENT_SECRET,\n    }),\n  ],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport AzureDevOps from \"@auth/qwik/providers/azure-devops\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      AzureDevOps({\n        clientId: import.meta.env.AUTH_AZURE_DEVOPS_APP_ID,\n        clientSecret: import.meta.env.AUTH_AZURE_DEVOPS_CLIENT_SECRET,\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport AzureDevOps from \"@auth/sveltekit/providers/azure-devops\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    AzureDevOps({\n      clientId: AUTH_AZURE_DEVOPS_APP_ID,\n      clientSecret: AUTH_AZURE_DEVOPS_CLIENT_SECRET,\n    }),\n  ],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport AzureDevOps from \"@auth/express/providers/azure-devops\"\n\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [\n      AzureDevOps({\n        clientId: AUTH_AZURE_DEVOPS_APP_ID,\n        clientSecret: AUTH_AZURE_DEVOPS_CLIENT_SECRET,\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Express>\n</Code>\n\n### Refresh token rotation\n\nUse the [main guide](/guides/refresh-token-rotation) as your starting point with the following considerations:\n\n```ts filename=\"./auth.ts\"\nexport const { signIn, signOut, auth } = NextAuth({\n  callbacks: {\n    async jwt({ token, user, account }) {\n      // The token has an absolute expiration time\n      const accessTokenExpires = account.expires_at * 1000\n    },\n  },\n})\n\nasync function refreshAccessToken(token) {\n  const response = await fetch(\n    \"https://app.vssps.visualstudio.com/oauth2/token\",\n    {\n      headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n      method: \"POST\",\n      body: new URLSearchParams({\n        client_assertion_type:\n          \"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\",\n        client_assertion: AZURE_DEVOPS_CLIENT_SECRET,\n        grant_type: \"refresh_token\",\n        assertion: token.refreshToken,\n        redirect_uri:\n          process.env.NEXTAUTH_URL + \"/api/auth/callback/azure-devops\",\n      }),\n    }\n  )\n\n  // The refreshed token comes with a relative expiration time\n  const accessTokenExpires = Date.now() + newToken.expires_in * 1000\n}\n```\n"
  },
  {
    "path": "docs/pages/getting-started/providers/bankid-no.mdx",
    "content": "---\ntitle: BankID Norge\n---\n\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/bankid-no.svg\" width=\"64\" height=\"64\" />\n\n# BankID Norway Provider\n\n[BankID Norge](https://bankid.no) is a widespread login method in Norway, used by banks, government agencies, and other organizations. This provider allows users to sign in with BankID Norway.\n\n## Resources\n\n- [BankID Norway documentation](https://confluence.bankidnorge.no/confluence/pdoidcl)\n- [BankID Testing](https://developer.bankid.no/bankid-with-biometrics/testing)\n- [BankID Public Testing discovery endpoint](https://auth.current.bankid.no/auth/realms/current/.well-known/openid-configuration)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/bankid-no\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/bankid-no\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/bankid-no\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_BANKID_NO_ID\nAUTH_BANKID_NO_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport BankIDNorway from \"next-auth/providers/bankid-no\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [BankIDNorway],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport BankIDNorway from \"@auth/qwik/providers/bankid-no\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [BankIDNorway],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport BankIDNorway from \"@auth/sveltekit/providers/bankid-no\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [BankIDNorway],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport BankIDNorway from \"@auth/express/providers/bankid-no\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [BankIDNorway] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/battlenet.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/battlenet.svg\" height=\"64\" width=\"64\" />\n\n# Battle.net Provider\n\n## Resources\n\n- [BattleNet OAuth documentation](https://develop.battle.net/documentation/guides/using-oauth)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/battlenet\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/battlenet\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/battlenet\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_BATTLENET_ID\nAUTH_BATTLENET_SECRET\nAUTH_BATTLENET_ISSUER\n```\n\nissuer must be one of these values, based on the available regions:\n\n```\ntype BattleNetIssuer =\n| \"https://www.battlenet.com.cn/oauth\"\n| \"https://us.battle.net/oauth\"\n| \"https://eu.battle.net/oauth\"\n| \"https://kr.battle.net/oauth\"\n| \"https://tw.battle.net/oauth\"\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\";\nimport BattleNet from \"next-auth/providers/battlenet\";\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [BattleNet({\n    clientId: AUTH_BATTLENET_CLIENT_ID\n    clientSecret: AUTH_BATTLENET_CLIENT_SECRET\n    issuer: AUTH_BATTLENET_ISSUER\n  })]\n});\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport BattleNet from \"@auth/qwik/providers/battlenet\";\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [BattleNet({\n      clientId: import.meta.env.AUTH_BATTLENET_CLIENT_ID\n      clientSecret: import.meta.env.AUTH_BATTLENET_CLIENT_SECRET\n      issuer: import.meta.env.AUTH_BATTLENET_ISSUER\n    })]\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\";\nimport BattleNet from \"@auth/sveltekit/providers/battlenet\";\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [BattleNet({\n    clientId: AUTH_BATTLENET_CLIENT_ID\n    clientSecret: AUTH_BATTLENET_CLIENT_SECRET\n    issuer: AUTH_BATTLENET_ISSUER\n  })]\n});\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\";\nimport BattleNet from \"@auth/express/providers/battlenet\";\n\napp.use(\"/auth/*\", ExpressAuth({\n  providers: [BattleNet({\n    clientId: AUTH_BATTLENET_CLIENT_ID\n    clientSecret: AUTH_BATTLENET_CLIENT_SECRET\n    issuer: AUTH_BATTLENET_ISSUER\n  })]\n}));\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/beyondidentity.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img\n  align=\"right\"\n  src=\"/img/providers/beyondidentity.svg\"\n  height=\"64\"\n  width=\"64\"\n/>\n\n# Beyond Identity Provider\n\n## Resources\n\n- [Beyond Identity Developer documentation](https://developer.beyondidentity.com/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/beyondidentity\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/beyondidentity\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/beyondidentity\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_BEYOND_IDENTITY_ID\nAUTH_BEYOND_IDENTITY_SECRET\nAUTH_BEYOND_IDENTITY_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport BeyondIdentity from \"next-auth/providers/beyondidentity\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [BeyondIdentity],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport BeyondIdentity from \"@auth/qwik/providers/beyondidentity\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [BeyondIdentity],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport BeyondIdentity from \"@auth/sveltekit/providers/beyondidentity\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [BeyondIdentity],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport BeyondIdentity from \"@auth/express/providers/beyondidentity\"\n\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [BeyondIdentity],\n  })\n)\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/bitbucket.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/bitbucket.svg\" height=\"64\" width=\"64\" />\n\n# Bitbucket Provider\n\n## Resources\n\n- [Using OAuth on Bitbucket Cloud](https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/)\n- [Bitbucket REST API Authentication](https://developer.atlassian.com/cloud/bitbucket/rest/intro/#authentication)\n- [Bitbucket REST API Users](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-users/#api-group-users)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/bitbucket\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/bitbucket\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/bitbucket\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n<Code>\n  <Code.Next>\n\n```bash filename=\".env.local\"\nAUTH_BITBUCKET_ID\nAUTH_BITBUCKET_SECRET\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash filename=\".env\"\nAUTH_BITBUCKET_ID\nAUTH_BITBUCKET_SECRET\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash filename=\".env\"\nAUTH_BITBUCKET_ID\nAUTH_BITBUCKET_SECRET\n```\n\n  </Code.Svelte>\n\n    <Code.Express>\n\n```bash filename=\".env\"\nAUTH_BITBUCKET_ID\nAUTH_BITBUCKET_SECRET\n```\n\n  </Code.Express>\n</Code>\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"@/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Bitbucket from \"next-auth/providers/bitbucket\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Bitbucket],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Bitbucket from \"@auth/qwik/providers/bitbucket\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Bitbucket],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Bitbucket from \"@auth/sveltekit/providers/bitbucket\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Bitbucket],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Bitbucket from \"@auth/express/providers/bitbucket\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Bitbucket] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/box.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/box.svg\" height=\"64\" width=\"64\" />\n\n# Box Provider\n\n## Resources\n\n- [Box developers documentation](https://developer.box.com/reference/)\n- [Box OAuth documentation](https://developer.box.com/guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-box/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/box\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/box\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/box\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_BOX_CLIENT_ID\nAUTH_BOX_CLIENT_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Box from \"next-auth/providers/box\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Box],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Box from \"@auth/qwik/providers/box\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Box],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Box from \"@auth/sveltekit/providers/box\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Box],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Box from \"@auth/express/providers/box\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Box] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/boxyhq-saml.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img\n  align=\"right\"\n  src=\"/img/providers/boxyhq-saml.svg\"\n  height=\"64\"\n  width=\"64\"\n/>\n\n# BoxyHQ SAML Provider\n\n## Resources\n\n- [BoxyHQ OAuth documentation](https://boxyhq.com/docs/jackson/overview)\n\n## Setup\n\nAdd BoxyHQ SAML login to your page.\n\nBoxyHQ SAML is an open source service that handles the SAML SSO login flow as an OAuth 2.0 flow, abstracting away all the complexities of the SAML protocol. Enable Enterprise single-sign-on in your app with ease.\n\nYou can deploy BoxyHQ SAML as a separate service or embed it into your app using our NPM library. [Check out the documentation for more details](https://boxyhq.com/docs/jackson/deploy)\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/boxyhq-saml\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/boxhq-saml\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/boxhq-saml\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_BOXYHQ_SAML_ID\nAUTH_BOXYHQ_SAML_SECRET\nAUTH_BOXYHQ_SAML_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport BoxyHQ from \"next-auth/providers/boxyhq-saml\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    BoxyHQ({\n      authorization: { params: { scope: \"\" } }, // This is needed for OAuth 2.0 flow, otherwise default to openid\n      clientId: AUTH_BOXYHQ_SAML_ID,\n      clientSecret: AUTH_BOXYHQ_SAML_SECRET,\n      issuer: AUTH_BOXYHQ_SAML_ISSUER,\n    }),\n  ],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport BoxyHQ from \"@auth/qwik/providers/boxyhq-saml\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      BoxyHQ({\n        authorization: { params: { scope: \"\" } }, // This is needed for OAuth 2.0 flow, otherwise default to openid\n        clientId: import.meta.env.AUTH_BOXYHQ_SAML_ID,\n        clientSecret: import.meta.env.AUTH_BOXYHQ_SAML_SECRET,\n        issuer: import.meta.env.AUTH_BOXYHQ_SAML_ISSUER,\n      }),  \n    ],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport BoxyHQ from \"@auth/sveltekit/providers/boxyhq-saml\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    BoxyHQ({\n      authorization: { params: { scope: \"\" } }, // This is needed for OAuth 2.0 flow, otherwise default to openid\n      clientId: AUTH_BOXYHQ_SAML_ID,\n      clientSecret: AUTH_BOXYHQ_SAML_SECRET,\n      issuer: AUTH_BOXYHQ_SAML_ISSUER,\n    }),\n  ],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport BoxyHQ from \"@auth/express/providers/boxyhq-saml\"\n\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [\n      BoxyHQ({\n        authorization: { params: { scope: \"\" } }, // This is needed for OAuth 2.0 flow, otherwise default to openid\n        clientId: AUTH_BOXYHQ_SAML_ID,\n        clientSecret: AUTH_BOXYHQ_SAML_SECRET,\n        issuer: AUTH_BOXYHQ_SAML_ISSUER,\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Express>\n</Code>\n\n### SAML\n\nSAML login requires a configuration for every tenant of yours. One common method is to use the domain for an email address to figure out which tenant they belong to. You can also use a unique tenant ID (string) from your backend for this, typically some kind of account or organization ID.\n\nCheck out the [documentation](https://boxyhq.com/docs/jackson/saml-flow#2-saml-config-api) for more details.\n\nOn the client side you'll need to pass additional parameters `tenant` and `product` to the `signIn` function. This will allow BoxyHQL SAML to figure out the right SAML configuration and take your user to the right SAML Identity Provider to sign them in.\n\n```ts\nimport { signIn } from \"next-auth/react\";\n\n// Map your users's email to a tenant and product\nconst tenant = email.split(\"@\")[1];\nconst product = 'my_awesome_product';\n\n<Button\n  onClick={async (event) => {\n    event.preventDefault();\n\n    signIn(\"boxyhq-saml\", {}, { tenant, product });\n  }}\n>\n```\n"
  },
  {
    "path": "docs/pages/getting-started/providers/bungie.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/bungie.svg\" height=\"64\" width=\"128\" />\n\n# Bungie Provider\n\n## Resources\n\n- [Bungie OAuth documentation](https://github.com/Bungie-net/api/wiki/OAuth-Documentation)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/bungie\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/bungie\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/bungie\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_BUNGIE_ID\nAUTH_BUNGIE_SECRET\nAUTH_BUNGIE_API_KEY\n```\n\n### Configuration\n\nNavigate to https://www.bungie.net/en/Application and fill in the required details:\n\n- Application name\n- Application Status\n- Website\n- OAuth Client Type\n  - Confidential\n- Redirect URL\n  - https://localhost:3000/api/auth/callback/bungie\n- Scope\n  - `Access items like your Bungie.net notifications, memberships, and recent Bungie.Net forum activity.`\n- Origin Header\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\";\nimport Bungie from \"next-auth/providers/boxyhq-saml\";\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Bungie({\n      clientId: AUTH_BUNGIE_ID\n      clientSecret: AUTH_BUNGIE_SECRET\n      headers: { \"X-API-Key\": AUTH_BUNGIE_API_KEY }\n    }),\n  ],\n});\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Bungie from \"@auth/qwik/providers/boxyhq-saml\";\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Bungie({\n        clientId: import.meta.env.AUTH_BUNGIE_ID\n        clientSecret: import.meta.env.AUTH_BUNGIE_SECRET\n        headers: { \"X-API-Key\": import.meta.env.AUTH_BUNGIE_API_KEY }\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\";\nimport Bungie from \"@auth/sveltekit/providers/boxyhq-saml\";\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    Bungie({\n      clientId: AUTH_BUNGIE_ID\n      clientSecret: AUTH_BUNGIE_SECRET\n      headers: { \"X-API-Key\": AUTH_BUNGIE_API_KEY }\n    }),\n  ],\n});\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\";\nimport Bungie from \"@auth/express/providers/boxyhq-saml\";\n\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [\n      Bungie({\n        clientId: AUTH_BUNGIE_ID\n        clientSecret: AUTH_BUNGIE_SECRET\n        headers: { \"X-API-Key\": AUTH_BUNGIE_API_KEY }\n      }),\n    ],\n  })\n);\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Bungie requires all clients to be using **https**.\n- Bungie does not allow the hostname `localhost`, so for local development, you must use `127.0.0.1` for example\n"
  },
  {
    "path": "docs/pages/getting-started/providers/click-up.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/clickup.svg\" height=\"64\" width=\"64\" />\n\n# Click-Up Provider\n\n## Resources\n\n- [ClickUp documentation: Authorizing OAuth Apps](https://clickup.com/api/developer-portal/authentication#oauth-flow)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/clickup\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/clickup\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/clickup\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_CLICKUP_ID\nAUTH_CLICKUP_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport ClickUp from \"next-auth/providers/click-up\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [ClickUp],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport ClickUp from \"@auth/qwik/providers/click-up\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [ClickUp],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport ClickUp from \"@auth/sveltekit/providers/click-up\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [ClickUp],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport ClickUp from \"@auth/express/providers/click-up\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [ClickUp] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/cognito.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/cognito.svg\" height=\"64\" width=\"64\" />\n\n# Cognito Provider\n\n## Resources\n\n- [Cognito Portal](https://console.aws.amazon.com/cognito/v2/home)\n- [Cognito OAuth documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-userpools-server-contract-reference.html)\n- [Cognito Hosted Domain](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-assign-domain.html)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/cognito\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/cognito\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/cognito\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_COGNITO_ID\nAUTH_COGNITO_SECRET\nAUTH_COGNITO_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Cognito from \"next-auth/providers/cognito\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Cognito],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Cognito from \"@auth/qwik/providers/cognito\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Cognito],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Cognito from \"@auth/sveltekit/providers/cognito\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Cognito],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Cognito from \"@auth/express/providers/cognito\"\n\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [Cognito],\n  })\n)\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\nYou need to select your AWS region to go the the Cognito dashboard.\n\n<Callout type=\"info\">\n  The issuer is a URL, that looks like this: `https://cognito-idp.{region}\n  .amazonaws.com/{PoolId}`, where `PoolId` is from General Settings in Cognito,\n  not to be confused with the App Client ID.\n</Callout>\n\n<Callout type=\"info\">\n  Before you can set these settings, you must set up an Amazon Cognito hosted\n  domain. The setting can be found in `App Client/Edit Hosted UI`.\n</Callout>\n\n<Callout type=\"warning\">\n  Make sure you select all the appropriate client settings or the OAuth flow\n  will not work.\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/coinbase.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/coinbase.svg\" height=\"64\" width=\"128\" />\n\n# Coinbase Provider\n\n## Resources\n\n- [Coinbase OAuth documentation](https://developers.coinbase.com/api/v2)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/coinbase\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/coinbase\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/coinbase\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_COINBASE_ID\nAUTH_COINBASE_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Coinbase from \"next-auth/providers/coinbase\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Coinbase],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Coinbase from \"@auth/qwik/providers/coinbase\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Coinbase],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Coinbase from \"@auth/sveltekit/providers/coinbase\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Coinbase],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Coinbase from \"@auth/express/providers/coinbase\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Coinbase] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- This Provider template has a 2 hour access token to it. A refresh token is also returned.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/credentials.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n# Credentials Provider\n\nThe Credentials provider allows you to handle signing in with arbitrary credentials, such as a username and password, domain, two factor authentication or hardware device (e.g. YubiKey U2F / FIDO).\n\nIt is intended to support use cases where you have an existing system you need to authenticate users against, and therefore users authenticated in this manner are not persisted in the database.\n\n## Resources\n\n- [Client-side Input Validation Example](/getting-started/authentication/credentials#verifying-data-with-zod)\n\n## Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Credentials from \"next-auth/providers/credentials\"\n\nexport const { signIn, signOut, auth } = NextAuth({\n  providers: [\n    Credentials({\n      credentials: {\n        username: { label: \"Username\" },\n        password: { label: \"Password\", type: \"password\" },\n      },\n      async authorize({ request }) {\n        const response = await fetch(request)\n        if (!response.ok) return null\n        return (await response.json()) ?? null\n      },\n    }),\n  ],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Credentials from \"@auth/qwik/providers/credentials\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Credentials({\n        credentials: {\n          username: { label: \"Username\" },\n          password: { label: \"Password\", type: \"password\" },\n        },\n        async authorize({ request }) {\n          const response = await fetch(request)\n          if (!response.ok) return null\n          return (await response.json()) ?? null\n        },\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Credentials from \"@auth/sveltekit/providers/credentials\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    Credentials({\n      credentials: {\n        username: { label: \"Username\" },\n        password: { label: \"Password\", type: \"password\" },\n      },\n      async authorize({ request }) {\n        const response = await fetch(request)\n        if (!response.ok) return null\n        return (await response.json()) ?? null\n      },\n    }),\n  ],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\";\nimport Credentials from \"@auth/express/providers/credentials\";\n\napp.use(\"/auth/*\", ExpressAuth({\n  providers: [\n    Credentials({\n      credentials: {\n        username: { label: \"Username\" },\n        password: { label: \"Password\", type: \"password\" },\n      },\n      async authorize({ request }) {\n        const response = await fetch(request);\n        if (!response.ok) return null;\n        return (await response.json()) ?? null;\n      },\n    }),\n  ],\n});\n```\n\n  </Code.Express>\n</Code>\n\n### Custom Error Messages\n\nYou can throw a custom error in the `authorize` function to return a custom error message to the user.\n\n```ts filename=\"@/auth.ts\" /InvalidLoginError/\nimport NextAuth, { CredentialsSignin } from \"next-auth\"\nimport Credentials from \"next-auth/providers/credentials\"\n\nclass InvalidLoginError extends CredentialsSignin {\n  code = \"Invalid identifier or password\"\n}\n\nexport const { handlers, auth } = NextAuth({\n  providers: [\n    Credentials({\n      credentials: {\n        username: { label: \"Username\" },\n        password: { label: \"Password\", type: \"password\" },\n      },\n      async authorize(credentials) {\n        throw new InvalidLoginError()\n      },\n    }),\n  ],\n})\n```\n\nYou will then receive that custom error code in the query parameters of the signin page your user returns to after a failed login attempt, for example `https://app.company.com/auth/signin?error=CredentialsSignin&code=Invalid+identifier+or+password`.\n\n<Callout type=\"warning\">\nOAuth providers spend significant amounts of money, time, and engineering effort to build:\n\n- abuse detection (bot-protection, rate-limiting)\n- password management (password reset, credential stuffing, rotation)\n- data security (encryption/salting, strength validation)\n\nand much more for authentication solutions. It is likely that your application would benefit from leveraging these battle-tested solutions rather than try to rebuild them from scratch.\n\nIf you'd still like to build password-based authentication for your application despite these risks, Auth.js gives you full control to do so.\n\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/descope.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/descope.svg\" width=\"64\" height=\"64\" />\n\n# Descope Provider\n\n## Resources\n\n- [Descope OIDC](https://docs.descope.com/manage/idpapplications/oidc/)\n- [Descope Flows](https://docs.descope.com/customize/flows)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/descope\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/descope\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/descope\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_DESCOPE_ID\nAUTH_DESCOPE_SECRET\nAUTH_DESCOPE_ISSUER\n```\n\n### Configuration\n\nFollow these steps:\n\n1. Log into the [Descope console](https://app.descope.com)\n2. Follow the [OIDC instructions](https://docs.descope.com/manage/idpapplications/oidc/)\n\nAdd the required environment variables from above to your `.env.local` file.\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Descope from \"next-auth/providers/descope\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Descope],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Descope from \"@auth/qwik/providers/descope\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Descope],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Descope from \"@auth/sveltekit/providers/descope\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Descope],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Descope from \"@auth/express/providers/descope\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Descope] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Using Descope Widgets\n\nIf you wish to use Descope [Widgets](https://docs.descope.com/widgets) with NextAuth.js, you will have to wrap your NextAuth.js components with our Next.js SDK and `<AuthProvider>`.\n\nFor more information on this, please look at our documentation [here](https://docs.descope.com/getting-started/nextauth/app-router#nextauth-and-widgets).\n"
  },
  {
    "path": "docs/pages/getting-started/providers/discord.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/discord.svg\" height=\"64\" width=\"64\" />\n\n# Discord Provider\n\n## Resources\n\n- [Discord OAuth documentation](https://discord.com/developers/docs/topics/oauth2)\n- [Discord OAuth apps](https://discord.com/developers/applications)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/discord\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/discord\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/discord\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_DISCORD_ID\nAUTH_DISCORD_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Discord from \"next-auth/providers/discord\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Discord],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Discord from \"@auth/qwik/providers/discord\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Discord],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Discord from \"@auth/sveltekit/providers/discord\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Discord],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Discord from \"@auth/express/providers/discord\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Discord] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/dribbble.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/dribbble.svg\" height=\"64\" width=\"64\" />\n\n# Dribbble Provider\n\n## Resources\n\n- [Dribbble API](https://developer.dribbble.com)\n- [Dribbble OAuth](https://developer.dribbble.com/v2/oauth/)\n- [Dribbble Applications](https://dribbble.com/account/applications/new)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/dribbble\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/dribbble\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/dribbble\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_DRIBBBLE_ID\nAUTH_DRIBBBLE_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Dribbble from \"next-auth/providers/dribbble\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Dribbble],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Dribbble from \"@auth/qwik/providers/dribbble\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Dribbble],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Dribbble from \"@auth/sveltekit/providers/dribbble\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Dribbble],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Dribbble from \"@auth/express/providers/dribbble\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Dribbble] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- You can optionally set the scope to `public upload` for more advanced scenarios. If omitted, the default `public` scope will be used for authentication purposes.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/dropbox.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/dropbox.svg\" height=\"64\" width=\"64\" />\n\n# Dropbox Provider\n\n## Resources\n\n- [Dropbox OAuth documentation](https://developers.dropbox.com/oauth-guide)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/dropbox\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/dropbox\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/dropbox\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_DROPBOX_ID\nAUTH_DROPBOX_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Dropbox from \"next-auth/providers/dropbox\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Dropbox],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Dropbox from \"@auth/qwik/providers/dropbox\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Dropbox],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Dropbox from \"@auth/sveltekit/providers/dropbox\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Dropbox],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Dropbox from \"@auth/express/providers/dropbox\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Dropbox] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/duende-identity-server6.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img\n  align=\"right\"\n  src=\"/img/providers/duende-identityserver6.svg\"\n  height=\"64\"\n  width=\"64\"\n/>\n\n# Duende Identity Server Provider\n\n## Resources\n\n- [DuendeIdentityServer6 documentation](https://docs.duendesoftware.com/identityserver/v6)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/duende-identity-service\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/duende-identity-service\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/duende-identity-service\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_DUENDE_IDENTITY_SERVER6_ID\nAUTH_DUENDE_IDENTITY_SERVER6_SECRET\nAUTH_DUENDE_IDENTITY_SERVER6_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport DuendeIdentityServer6 from \"next-auth/providers/duende-identity-server6\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [DuendeIdentityServer6],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport DuendeIdentityServer6 from \"@auth/qwik/providers/duende-identity-server6\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [DuendeIdentityServer6],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport DuendeIdentityServer6 from \"@auth/sveltekit/providers/duende-identity-server6\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [DuendeIdentityServer6],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport DuendeIdentityServer6 from \"@auth/express/providers/duende-identity-server6\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [DuendeIdentityServer6] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Demo IdentityServer\n\nThe configuration below is for the demo server at https://demo.duendesoftware.com/\n\nIf you want to try it out, you can copy and paste the configuration below.\n\nYou can sign in to the demo service with either `bob`/`bob` or `alice`/`alice`.\n\n```js filename=pages/api/auth/[...nextauth].js\nimport DuendeIDS6Provider from \"next-auth/providers/duende-identity-server6\"\nproviders: [\n  DuendeIDS6Provider({\n    clientId: \"interactive.confidential\",\n    clientSecret: \"secret\",\n    issuer: \"https://demo.duendesoftware.com\",\n  }),\n]\n```\n"
  },
  {
    "path": "docs/pages/getting-started/providers/eveonline.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/eveonline.svg\" height=\"64\" width=\"128\" />\n\n# EVEOnline Provider\n\n## Resources\n\n- [EveOnline OAuth documentation](https://developers.eveonline.com/blog/article/sso-to-authenticated-calls)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/eveonline\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/eveonline\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/eveonline\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_EVEONLINE_ID\nAUTH_EVEONLINE_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport EveOnline from \"next-auth/providers/eve-online\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [EveOnline],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport EveOnline from \"@auth/qwik/providers/eve-online\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [EveOnline],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport EveOnline from \"@auth/sveltekit/providers/eve-online\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [EveOnline],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport EveOnline from \"@auth/express/providers/eve-online\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [EveOnline] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- When creating your application, make sure to select `Authentication & API Access` as the connection type. Also ensure that the `publicData` scope is selected.\n\n- If using JWT for the session, you can add the `CharacterID` to the JWT and session. For example:\n\n```ts\nconst AuthConfig = {\n  callbacks: {\n    jwt({ token, profile }) {\n      if (profile) {\n        token.characterId = profile.CharacterID\n      }\n      return token\n    },\n    session({ session, token }) {\n      session.user.characterId = token.characterId\n      return session\n    },\n  },\n}\n```\n"
  },
  {
    "path": "docs/pages/getting-started/providers/facebook.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/facebook.svg\" height=\"64\" width=\"64\" />\n\n# Facebook Provider\n\n## Resources\n\n- [Facebook OAuth documentation](https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/)\n- [Facebook Developer Apps](https://developers.facebook.com/apps/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/facebook\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/facebook\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/facebook\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_FACEBOOK_ID\nAUTH_FACEBOOK_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Facebook from \"next-auth/providers/facebook\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Facebook],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Facebook from \"@auth/qwik/providers/facebook\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Facebook],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Facebook from \"@auth/sveltekit/providers/facebook\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Facebook],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Facebook from \"@auth/express/providers/facebook\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Facebook] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Production applications cannot use localhost URLs to sign in with Facebook. You need to use a dedicated development application in Facebook to use localhost callback URLs.\n- Email address may not be returned for accounts created on mobile.\n- `clientId` is your Facebook App ID, `clientSecret` is your Facebook App Secret.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/faceit.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/faceit.svg\" height=\"64\" width=\"64\" />\n\n# Faceit Provider\n\n## Resources\n\n- [FACEIT OAuth documentation](https://cdn.faceit.com/third_party/docs/FACEIT_Connect_3.0.pdf)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/faceit\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/faceit\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/faceit\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_FACEIT_ID\nAUTH_FACEIT_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport FaceIt from \"next-auth/providers/faceit\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [FaceIt],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport FaceIt from \"@auth/qwik/providers/faceit\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [FaceIt],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport FaceIt from \"@auth/sveltekit/providers/faceit\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [FaceIt],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport FaceIt from \"@auth/express/providers/faceit\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [FaceIt] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Grant type: `Authorization Code`\n- Scopes required to get basic infos like email, nickname, guid and avatar: `openid, email, profile`\n"
  },
  {
    "path": "docs/pages/getting-started/providers/figma.mdx",
    "content": "import { Code } from \"@/components/Code\"\nimport { Callout } from \"nextra/components\"\n\n<img align=\"right\" src=\"/img/providers/figma.svg\" height=\"64\" width=\"64\" />\n\n# Figma Provider\n\n## Resources\n\n- [Using OAuth 2 on Figma](https://www.figma.com/developers/api#oauth2)\n- [User Type](https://www.figma.com/developers/api#users-types)\n- [Scopes](https://www.figma.com/developers/api#authentication-scopes)\n- [Migrate](https://www.figma.com/developers/api#oauth_migration_guide)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/figma\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/figma\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/figma\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n<Code>\n  <Code.Next>\n\n```bash filename=\".env.local\"\nAUTH_FIGMA_ID\nAUTH_FIGMA_SECRET\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash filename=\".env\"\nAUTH_FIGMA_ID\nAUTH_FIGMA_SECRET\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash filename=\".env\"\nAUTH_FIGMA_ID\nAUTH_FIGMA_SECRET\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```bash filename=\".env\"\nAUTH_FIGMA_ID\nAUTH_FIGMA_SECRET\n```\n\n  </Code.Express>\n</Code>\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"@/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Figma from \"next-auth/providers/figma\"\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Figma],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Figma from \"@auth/qwik/providers/figma\"\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Figma],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Figma from \"@auth/sveltekit/providers/figma\"\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Figma],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Figma from \"@auth/express/providers/figma\"\napp.use(\"/auth/*\", ExpressAuth({ providers: [Figma] }))\n```\n\n  </Code.Express>\n</Code>\n\n<Callout type=\"warning\">\n  The URL must be accessed via the user's browser and not an embedded webview\n  inside your application. Webview access to the Figma OAuth flow is not\n  supported and may stop working for some or all users without an API version\n  update.\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/forwardemail.mdx",
    "content": "import { Callout, Tabs } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img\n  align=\"right\"\n  src=\"/img/providers/forwardemail.svg\"\n  className=\"dark:bg-[currentColor]\"\n  height=\"64\"\n  width=\"96\"\n/>\n\n# Forward Email Provider\n\n## Overview\n\nThe Forward Email provider uses email to send \"magic links\" that contain URLs with verification tokens can be used to sign in.\n\nAdding support for signing in via email in addition to one or more OAuth services provides a way for users to sign in if they lose access to their OAuth account (e.g. if it is locked or deleted).\n\nThe Forward Email provider can be used in conjunction with (or instead of) one or more OAuth providers.\n\n### How it works\n\nOn initial sign in, a **Verification Token** is sent to the email address provided. By default this token is valid for 24 hours. If the Verification Token is used within that time (i.e. by clicking on the link in the email) an account is created for the user and they are signed in.\n\nIf someone provides the email address of an _existing account_ when signing in, an email is sent and they are signed into the account associated with that email address when they follow the link in the email.\n\n<Callout type=\"warning\">\n  The Forward Email provider can be used with both BasicAuth and database\n  managed sessions, however **you must configure a database** to use it. It is\n  not possible to enable email sign in without using a database.\n</Callout>\n\n## Configuration\n\n1. First, you'll need to [add your domain](https://forwardemail.net/my-account/domains) to your Forward Email account. This is required by Forward Email and this domain of the address you use in the `from` provider option.\n\n2. Next, you will have to generate an API key in the [My Account &rarr; Security](https://forwardemail.net/my-account/security). You can save this API key as the `AUTH_FORWARDEMAIL_KEY` environment variable.\n\n```sh\nAUTH_FORWARDEMAIL_KEY=abc\n```\n\nIf you name your environment variable `AUTH_FORWARDEMAIL_KEY`, the provider will pick it up automatically and your Auth.js configuration object can be simpler. If you'd like to rename it to something else, however, you'll have to manually pass it into the provider in your Auth.js configuration.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport ForwardEmail from \"next-auth/providers/forwardemail\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: ...,\n  providers: [\n    ForwardEmail({\n      // If your environment variable is named differently than default\n      apiKey: AUTH_FORWARDEMAIL_KEY,\n      from: \"no-reply@company.com\"\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport ForwardEmail from \"@auth/qwik/providers/forwardemail\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      ForwardEmail({\n        // If your environment variable is named differently than default\n        apiKey: import.meta.env.AUTH_FORWARDEMAIL_KEY,\n        from: \"no-reply@company.com\",\n      }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport ForwardEmail from \"@auth/sveltekit/providers/forwardemail\"\nimport { env } from \"$env/dynamic/prviate\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: ...,\n  providers: [\n    ForwardEmail({\n      // If your environment variable is named differently than default\n      apiKey: env.AUTH_FORWARDEMAIL_KEY,\n      from: \"no-reply@company.com\",\n    }),\n  ],\n})\n```\n\n</Code.Svelte>\n</Code>\n\n4. Do not forget to setup one of the [database adapters](https://authjs.dev/getting-started/database) for storing the Email verification token.\n\n5. You can now start the sign-in process with an email address at `/api/auth/signin`.\n\nA user account (i.e. an entry in the `Users` table) will not be created for the user until the first time they verify their email address. If an email address is already associated with an account, the user will be signed in to that account when they click the link in magic link email and use up the verification token.\n\n## Customization\n\n### Email Body\n\nYou can fully customize the sign in email that is sent by passing a custom function as the `sendVerificationRequest` option to `ForwardEmail()`.\n\n```js {7} filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport ForwardEmail from \"next-auth/providers/forwardemail\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    ForwardEmail({\n      server: process.env.EMAIL_SERVER,\n      from: process.env.EMAIL_FROM,\n      sendVerificationRequest({\n        identifier: email,\n        url,\n        provider: { server, from },\n      }) {\n        // your function\n      },\n    }),\n  ],\n})\n```\n\nAs an example, the following shows the source for our built-in `sendVerificationRequest()` method. Notice that we're rendering the HTML (`html()`) and making the network call (`fetch()`) to Forward Email to actually do the sending here in this method.\n\n```ts filename=\"./lib/authSendRequest.ts\" {4, 14}\nexport async function sendVerificationRequest(params) {\n  const { identifier: to, provider, url, theme } = params\n  const { host } = new URL(url)\n  const res = await fetch(\"https://api.forwardemail.net/v1/emails\", {\n    method: \"POST\",\n    headers: {\n      Authorization: `Basic ${btoa(provider.apiKey + \":\")}`,\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify({\n      from: provider.from,\n      to,\n      subject: `Sign in to ${host}`,\n      html: html({ url, host, theme }),\n      text: text({ url, host }),\n    }),\n  })\n\n  if (!res.ok)\n    throw new Error(\"Forward Email error: \" + JSON.stringify(await res.json()))\n}\n\nfunction html(params: { url: string; host: string; theme: Theme }) {\n  const { url, host, theme } = params\n\n  const escapedHost = host.replace(/\\./g, \"&#8203;.\")\n\n  const brandColor = theme.brandColor || \"#346df1\"\n  const color = {\n    background: \"#f9f9f9\",\n    text: \"#444\",\n    mainBackground: \"#fff\",\n    buttonBackground: brandColor,\n    buttonBorder: brandColor,\n    buttonText: theme.buttonText || \"#fff\",\n  }\n\n  return `\n<body style=\"background: ${color.background};\">\n  <table width=\"100%\" border=\"0\" cellspacing=\"20\" cellpadding=\"0\"\n    style=\"background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;\">\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        Sign in to <strong>${escapedHost}</strong>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\" style=\"padding: 20px 0;\">\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n          <tr>\n            <td align=\"center\" style=\"border-radius: 5px;\" bgcolor=\"${color.buttonBackground}\"><a href=\"${url}\"\n                target=\"_blank\"\n                style=\"font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;\">Sign\n                in</a></td>\n          </tr>\n        </table>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        If you did not request this email you can safely ignore it.\n      </td>\n    </tr>\n  </table>\n</body>\n`\n}\n\n// Email Text body (fallback for email clients that don't render HTML, e.g. feature phones)\nfunction text({ url, host }: { url: string; host: string }) {\n  return `Sign in to ${host}\\n${url}\\n\\n`\n}\n```\n\n<Callout type=\"info\">\n  If you want to generate great looking emails with React that are compatible\n  with many email clients, check out [mjml](https://mjml.io) or\n  [react-email](https://react.email)\n</Callout>\n\n### Verification Tokens\n\nBy default, we are generating a random verification token. You can define a `generateVerificationToken` method in your provider options if you want to override it:\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport ForwardEmail from \"next-auth/providers/forwardemail\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    ForwardEmail({\n      async generateVerificationToken() {\n        return crypto.randomUUID()\n      },\n    }),\n  ],\n})\n```\n\n### Normalizing Email Addresses\n\nBy default, Auth.js will normalize the email address. It treats the address as case-insensitive (which is technically not compliant to the [RFC 2821 spec](https://datatracker.ietf.org/doc/html/rfc2821), but in practice this causes more problems than it solves, i.e. when looking up users by e-mail from databases.) and also removes any secondary email address that may have been passed in as a comma-separated list. You can apply your own normalization via the `normalizeIdentifier` method on the `ForwardEmail` provider. The following example shows the default behavior:\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport ForwardEmail from \"next-auth/providers/forwardemail\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    ForwardEmail({\n      normalizeIdentifier(identifier: string): string {\n        // Get the first two elements only,\n        // separated by `@` from user input.\n        let [local, domain] = identifier.toLowerCase().trim().split(\"@\")\n        // The part before \"@\" can contain a \",\"\n        // but we remove it on the domain part\n        domain = domain.split(\",\")[0]\n        return `${local}@${domain}`\n\n        // You can also throw an error, which will redirect the user\n        // to the sign-in page with error=EmailSignin in the URL\n        // if (identifier.split(\"@\").length > 2) {\n        //   throw new Error(\"Only one email allowed\")\n        // }\n      },\n    }),\n  ],\n})\n```\n\n<Callout type=\"warning\">\n  Always make sure this returns a single e-mail address, even if multiple ones\n  were passed in.\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/foursquare.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/foursquare.svg\" height=\"64\" width=\"64\" />\n\n# Foursquare Provider\n\n## Resources\n\n- [Foursquare OAuth documentation](https://docs.foursquare.com/developer/reference/personalization-apis-authentication#web-app-authentication)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/foursquare\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/foursquare\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/foursquare\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_FOURSQUARE__ID\nAUTH_FOURSQUARE_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport FourSquare from \"next-auth/providers/foursquare\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [FourSquare],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport FourSquare from \"@auth/qwik/providers/foursquare\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [FourSquare],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport FourSquare from \"@auth/sveltekit/providers/foursquare\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [FourSquare],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport FourSquare from \"@auth/express/providers/foursquare\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [FourSquare] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Foursquare requires an additional apiVersion parameter in YYYYMMDD format, which essentially states \"I'm prepared for API changes up to this date\".\n"
  },
  {
    "path": "docs/pages/getting-started/providers/freshbooks.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/freshbooks.svg\" height=\"64\" width=\"64\" />\n\n# Freshbooks Provider\n\n## Resources\n\n- [FreshBooks OAuth documentation](https://developer.freshbooks.com/docs/places-api/authentication/#web-applications)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/freshbooks\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/freshbooks\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/freshbooks\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_FRESHBOOKS_ID\nAUTH_FRESHBOOKS_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport FreshBooks from \"next-auth/providers/freshbooks\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [FreshBooks],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport FreshBooks from \"@auth/qwik/providers/freshbooks\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [FreshBooks],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport FreshBooks from \"@auth/sveltekit/providers/freshbooks\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [FreshBooks],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport FreshBooks from \"@auth/express/providers/freshbooks\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [FreshBooks] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Freshbooks requires an additional apiVersion parameter in YYYYMMDD format, which essentially states \"I'm prepared for API changes up to this date\".\n"
  },
  {
    "path": "docs/pages/getting-started/providers/frontegg.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/frontegg.svg\" width=\"64\" height=\"64\" />\n\n# Frontegg Provider\n\n## Resources\n\n- [Frontegg documentation](https://docs.frontegg.com/docs/how-to-use-our-docs)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n    ```bash\n    https://example.com/api/auth/callback/frontegg\n    ```\n\n  </Code.Next>\n  <Code.Svelte>\n\n    ```bash\n    https://example.com/auth/callback/frontegg\n    ```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_FRONTEGG_ID\nAUTH_FRONTEGG_SECRET\nAUTH_FRONTEGG_ISSUER\n```\n\n### Configuration\n\nFollow these steps:\n\nLog into the [Frontegg portal](https://portal.frontegg.com)\n\nAdd the required environment variables to your `.env.local` file.\n\n```\n# Environments > Your environment > Env settings\nAUTH_FRONTEGG_ID=\"<Client ID>\"\n# Environments > Your environment > Env settings\nAUTH_FRONTEGG_SECRET=\"<API KEY>\"\n# Environments > Your environment > Env settings > Domains > Domain name\nAUTH_FRONTEGG_ISSUER=\"<https://[YOUR_SUBDOMAIN].frontegg.com>\"\n```\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Frontegg from \"next-auth/providers/frontegg\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Frontegg],\n})\n```\n\n  </Code.Next>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Frontegg from \"@auth/sveltekit/providers/frontegg\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Frontegg],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Frontegg from \"@auth/express/providers/frontegg\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Frontegg] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/fusionauth.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/fusionauth.svg\" height=\"64\" width=\"64\" />\n\n# Fusion Auth\n\n## Resources\n\n- [FusionAuth OAuth documentation](https://fusionauth.io/docs/v1/tech/oauth/)\n- [FusionAuth 5-minute setup guide](https://fusionauth.io/docs/v1/tech/5-minute-setup-guide).\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/fusionauth\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/fusionauth\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/fusionauth\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_FUSIONAUTH_ID\nAUTH_FUSIONAUTH_SECRET\nAUTH_FUSIONAUTH_TENANT_ID\nAUTH_FUSIONAUTH_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport FusionAuth from \"next-auth/providers/fusionauth\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    FusionAuth({\n      clientId: process.env.AUTH_FUSIONAUTH_ID,\n      clientSecret: process.env.AUTH_FUSIONAUTH_SECRET,\n      tenantId: process.env.AUTH_FUSIONAUTH_TENANT_ID,\n      issuer: process.env.AUTH_FUSIONAUTH_ISSUER,\n    }),\n  ],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport FusionAuth from \"@auth/qwik/providers/fusionauth\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      FusionAuth({\n        clientId: import.meta.env.AUTH_FUSIONAUTH_ID,\n        clientSecret: import.meta.env.AUTH_FUSIONAUTH_SECRET,\n        tenantId: import.meta.env.AUTH_FUSIONAUTH_TENANT_ID,\n        issuer: import.meta.env.AUTH_FUSIONAUTH_ISSUER,\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport FusionAuth from \"@auth/sveltekit/providers/fusionauth\"\nimport {\n  AUTH_FUSIONAUTH_ID,\n  AUTH_FUSIONAUTH_SECRET,\n  AUTH_FUSIONAUTH_TENANT_ID,\n  AUTH_FUSIONAUTH_ISSUER,\n} from \"$env/static/private\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    FusionAuth({\n      clientId: AUTH_FUSIONAUTH_ID,\n      clientSecret: AUTH_FUSIONAUTH_SECRET,\n      tenantId: AUTH_FUSIONAUTH_TENANT_ID,\n      issuer: AUTH_FUSIONAUTH_ISSUER,\n    }),\n  ],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport FusionAuth from \"@auth/express/providers/fusionauth\"\n\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [\n      FusionAuth({\n        clientId: process.env.AUTH_FUSIONAUTH_ID,\n        clientSecret: process.env.AUTH_FUSIONAUTH_SECRET,\n        tenantId: process.env.AUTH_FUSIONAUTH_TENANT_ID,\n        issuer: process.env.AUTH_FUSIONAUTH_ISSUER,\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Express>\n</Code>\n\n<Callout type=\"warning\">\n  If you're using multi-tenancy, you need to pass in the tenantId option to\n  apply the proper theme.\n</Callout>\n\n### Notes\n\n- An application can be created at `https://your-fusionauth-server-url/admin/application`\n\nIn the OAuth settings for your application, configure the following.\n\n- Redirect URL\n  - https://localhost:3000/api/auth/callback/fusionauth\n- Enabled grants\n  - Make sure _Authorization Code_ is enabled.\n\nIf using JSON Web Tokens, you need to make sure the signing algorithm is RS256, you can create an RS256 key pair by going to Settings, Key Master, generate RSA and choosing SHA-256 as algorithm. After that, go to the JWT settings of your application and select this key as Access Token signing key and Id Token signing key.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/github.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/github.svg\" height=\"64\" width=\"64\" />\n\n# GitHub Provider\n\n## Resources\n\n- [GitHub - Creating an OAuth App](https://docs.github.com/en/developers/apps/building-oauth-apps/creating-an-oauth-app)\n- [GitHub - Authorizing OAuth Apps](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps)\n- [GitHub - Configure your GitHub OAuth Apps](https://github.com/settings/developers)\n- [Learn more about OAuth](https://authjs.dev/concepts/oauth)\n- [Source code](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/github.ts)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/github\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/github\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/github\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_GITHUB_ID\nAUTH_GITHUB_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport GitHub from \"next-auth/providers/github\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [GitHub],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport GitHub from \"@auth/qwik/providers/github\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [GitHub],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport GitHub from \"@auth/sveltekit/providers/github\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [GitHub],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport GitHub from \"@auth/express/providers/github\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [GitHub] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/gitlab.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/gitlab.svg\" height=\"64\" width=\"64\" />\n\n# GitLab Provider\n\n## Resources\n\n- [GitLab OAuth documentation](https://docs.gitlab.com/ee/api/oauth2.html)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/gitlab\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/gitlab\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/gitlab\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_GITLAB_ID\nAUTH_GITLAB_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport GitLab from \"next-auth/providers/gitlab\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    // Default (gitlab.com)\n    GitLab,\n    // Self-hosted example\n    GitLab({\n      baseUrl: \"https://gitlab.example.com\",\n    }),\n  ],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport GitLab from \"@auth/qwik/providers/gitlab\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      GitLab,\n      GitLab({\n        instance: {\n          baseUrl: \"https://gitlab.example.com\"\n        }\n      })\n    ],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport GitLab from \"@auth/sveltekit/providers/gitlab\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    GitLab,\n    GitLab({\n      baseUrl: \"https://gitlab.example.com\",\n    }),\n  ],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport GitLab from \"@auth/express/providers/gitlab\"\n\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [\n      GitLab,\n      GitLab({\n        baseUrl: \"https://gitlab.example.com\",\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Enable the `read_user` option in scope if you want to save the users email address on sign up.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/google.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/google.svg\" height=\"64\" width=\"64\" />\n\n# Google Provider\n\n## Resources\n\n- [Google OAuth documentation](https://developers.google.com/identity/protocols/oauth2)\n- [Google OAuth Configuration](https://console.developers.google.com/apis/credentials)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/google\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/google\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/google\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_GOOGLE_ID\nAUTH_GOOGLE_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"@/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Google from \"next-auth/providers/google\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Google],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Google from \"@auth/qwik/providers/google\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Google],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Google from \"@auth/sveltekit/providers/google\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Google],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Google from \"@auth/express/providers/google\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Google] }))\n```\n\n  </Code.Express>\n</Code>\n\n## Notes\n\n### Refresh Token\n\nGoogle only provides Refresh Token to an application the first time a user signs in.\n\nTo force Google to re-issue a Refresh Token, the user needs to remove the application from their account and sign in again:\nhttps://myaccount.google.com/permissions\n\nAlternatively, you can also pass options in the `params` object of `authorization` which will force the Refresh Token to always be provided on sign in, however this will ask all users to confirm if they wish to grant your application access every time they sign in.\n\nIf you need access to the RefreshToken or AccessToken for a Google account and you are not using a database to persist user accounts, this may be something you need to do.\n\n```ts filename=\"./auth.ts\"\nimport Google from \"next-auth/providers/google\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Google({\n      authorization: {\n        params: {\n          prompt: \"consent\",\n          access_type: \"offline\",\n          response_type: \"code\",\n        },\n      },\n    }),\n  ],\n})\n```\n\nFor more information on exchanging a code for an access token and refresh token see the [Google OAuth documentation](https://developers.google.com/identity/openid-connect/openid-connect#exchangecode).\n\n### Email Verified\n\nGoogle also returns a `email_verified` boolean property in the OAuth profile.\n\nYou can use this property to restrict access to people with verified accounts at a particular domain.\n\n```ts filename=\"@/auth.ts\"\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  callbacks: {\n    async signIn({ account, profile }) {\n      if (account.provider === \"google\") {\n        return profile.email_verified && profile.email.endsWith(\"@example.com\")\n      }\n      return true // Do different verification for other providers that don't have `email_verified`\n    },\n  },\n})\n```\n"
  },
  {
    "path": "docs/pages/getting-started/providers/hubspot.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/hubspot.svg\" height=\"64\" width=\"64\" />\n\n# Hubspot Provider\n\n## Resources\n\n- [HubSpot OAuth documentation](https://developers.hubspot.com/docs/api/oauth-quickstart-guide)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/hubspot\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/hubspot\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/hubspot\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_HUBSPOT_ID\nAUTH_HUBSPOT_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Hubspot from \"next-auth/providers/hubspot\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Hubspot],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Hubspot from \"@auth/qwik/providers/hubspot\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Hubspot],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Hubspot from \"@auth/sveltekit/providers/hubspot\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Hubspot],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Hubspot from \"@auth/express/providers/hubspot\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Hubspot] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- HubSpot returns a limited amount of information on the token holder (see [docs](https://legacydocs.hubspot.com/docs/methods/oauth2/get-access-token-information)).\n- One other issue is that the name and profile photo cannot be fetched through API as discussed [here](https://community.hubspot.com/t5/APIs-Integrations/Profile-photo-is-not-retrieved-with-User-API/m-p/325521).\n"
  },
  {
    "path": "docs/pages/getting-started/providers/identity-server4.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img\n  align=\"right\"\n  src=\"/img/providers/identity-server4.svg\"\n  height=\"64\"\n  width=\"64\"\n/>\n\n# Identity Server Provider\n\n<Callout>\n  This provider has been deprecated and is superceded by [Duende\n  IdentityServer6](/getting-started/providers/duende-identity-server6).\n</Callout>\n\n## Resources\n\n- [IdentityServer4 OAuth documentation](https://identityserver4.readthedocs.io/en/latest/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/identity-server4\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/identity-server4\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/identity-server4\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_IDENTITY_SERVER4_ID\nAUTH_IDENTITY_SERVER4_SECRET\nAUTH_IDENTITY_SERVER4_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport IdentityServer4 from \"next-auth/providers/identity-server4\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [IdentityServer4],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport IdentityServer4 from \"@auth/qwik/providers/identity-server4\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [IdentityServer4],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport IdentityServer4 from \"@auth/sveltekit/providers/identity-server4\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [IdentityServer4],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport IdentityServer4 from \"@auth/express/providers/identity-server4\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [IdentityServer4] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/instagram.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/instagram.svg\" height=\"64\" width=\"64\" />\n\n# Instagram Provider\n\n## Resources\n\n- [Instagram OAuth documentation](https://developers.facebook.com/docs/instagram-basic-display-api/getting-started)\n- [Instagram OAuth apps](https://developers.facebook.com/apps/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/instagram\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/instagram\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/instagram\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_INSTAGRAM_ID\nAUTH_INSTAGRAM_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Instagram from \"next-auth/providers/instagram\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Instagram],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Instagram from \"@auth/qwik/providers/instagram\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Instagram],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Instagram from \"@auth/sveltekit/providers/instagram\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Instagram],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Instagram from \"@auth/express/providers/instagram\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Instagram] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Email address is not returned by the Instagram API.\n- Instagram requires a callback URL to be configured in your Facebook app and Facebook requires you to use **https** even for localhost. In order to do that, you either need to [add an SSL to your localhost](https://www.freecodecamp.org/news/how-to-get-https-working-on-your-local-development-environment-in-5-minutes-7af615770eec/) or use a proxy such as [ngrok](https://ngrok.com/docs).\n"
  },
  {
    "path": "docs/pages/getting-started/providers/kakao.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/kakao.svg\" height=\"64\" width=\"64\" />\n\n# Kakao Provider\n\n## Resources\n\n- [Kakao OAuth documentation](https://developers.kakao.com/product/kakaoLogin)\n- [Kakao OAuth configuration](https://developers.kakao.com/docs/latest/en/kakaologin/common)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/kakao\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/kakao\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/kakao\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_KAKAO_ID\nAUTH_KAKAO_SECRET\n```\n\n### Configuration\n\nCreate a provider and a Kakao application at https://developers.kakao.com/console/app. In the settings of the app under Kakao Login, activate web app, change consent items and configure callback URL.\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Kakao from \"next-auth/providers/kakao\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Kakao],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Kakao from \"@auth/qwik/providers/kakao\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Kakao],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Kakao from \"@auth/sveltekit/providers/kakao\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Kakao],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Kakao from \"@auth/express/providers/kakao\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Kakao] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- The \"Authorized redirect URIs\" used when creating the credentials must include your full domain and end in the callback path as shown above.\n\n![스크린샷 2023-11-28 오후 9 27 41](https://github.com/nextauthjs/next-auth/assets/66895208/7d4c2df6-45a6-4937-bb10-4b47c987bff4)\n\n- For production: `https://{YOUR_DOMAIN}/api/auth/callback/kakao`\n- For development: `http://localhost:3000/api/auth/callback/kakao`\n\n- Kakao's client key is in **Summary(It is written as 요약정보 in Korean.) tab's App Keys Field** (My Application > App Settings > Summary)\n\n![스크린샷 2023-11-28 오후 9 47 17](https://github.com/nextauthjs/next-auth/assets/66895208/a87e5705-26b9-4f83-99d7-6df097a3632c)\n\n- Kakao's clientSecret key is in **Security(It is written as 보안 in Korean.) tab's App Keys Field** (My Application > Product Settings > Kakao Login > Security)\n\n![스크린샷 2023-11-28 오후 9 38 25](https://github.com/nextauthjs/next-auth/assets/66895208/6a763921-4f70-40f4-a3e1-9abc78276d45)\n\n- Kakao dev console has a button at the top right to change from KR to ENG\n"
  },
  {
    "path": "docs/pages/getting-started/providers/keycloak.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/keycloak.svg\" height=\"64\" width=\"64\" />\n\n# Keycloak Provider\n\n## Resources\n\n- [Keycloak OIDC documentation](https://www.keycloak.org/docs/latest/server_admin/#_oidc_clients)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/keycloak\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/keycloak\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/keycloak\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_KEYCLOAK_ID\nAUTH_KEYCLOAK_SECRET\nAUTH_KEYCLOAK_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Keycloak from \"next-auth/providers/keycloak\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Keycloak],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Keycloak from \"@auth/qwik/providers/keycloak\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Keycloak],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Keycloak from \"@auth/sveltekit/providers/keycloak\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Keycloak],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Keycloak from \"@auth/express/providers/keycloak\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Keycloak] }))\n```\n\n  </Code.Express>\n</Code>\n\nEnable the \"Client Authentication\" option to retrieve your client secret in the Credentials tab.\n\nPrior to v20, create an `openid-connect` client in Keycloak with \"confidential\" as the \"Access Type\".\n\n- Issuer should include the realm – e.g. `https://my-keycloak-domain.com/realms/My_Realm`\n"
  },
  {
    "path": "docs/pages/getting-started/providers/line.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/line.svg\" height=\"64\" width=\"64\" />\n\n# Line Provider\n\n## Resources\n\n- [LINE Login documentation](https://developers.line.biz/en/docs/line-login/integrate-line-login/)\n- [LINE app console](https://developers.line.biz/console/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/line\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/line\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/line\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_LINE_ID\nAUTH_LINE_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Line from \"next-auth/providers/line\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Line],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Line from \"@auth/qwik/providers/line\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Line],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Line from \"@auth/sveltekit/providers/line\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Line],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Line from \"@auth/express/providers/line\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Line] }))\n```\n\n  </Code.Express>\n</Code>\n\nCreate a provider and a LINE login channel at https://developers.line.biz/console/. In the settings of the channel under LINE Login, activate web app and configure your callback URL as defined above.\n\n### Notes\n\n- To retrieve email address, you need to apply for Email address permission. Open [Line Developer Console](https://developers.line.biz/console/), go to your Login Channel. Scroll down bottom to find **OpenID Connect** -> **Email address permission**. Click **Apply** and follow the instruction.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/linkedin.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/linkedin.svg\" height=\"64\" width=\"64\" />\n\n# LinkedIn Provider\n\n## Resources\n\n- [LinkedIn OAuth documentation](https://docs.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow)\n- [LinkedIn app console](https://www.linkedin.com/developers/apps/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/linkedin\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/linkedin\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/linkedin\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_LINKEDIN_ID\nAUTH_LINKEDIN_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport LinkedIn from \"next-auth/providers/linkedin\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [LinkedIn],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport LinkedIn from \"@auth/qwik/providers/linkedin\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [LinkedIn],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport LinkedIn from \"@auth/sveltekit/providers/linkedin\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [LinkedIn],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport LinkedIn from \"@auth/express/providers/linkedin\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [LinkedIn] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/logto.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/logto.svg\" width=\"64\" height=\"64\" />\n\n# Logto Provider\n\n## Resources\n\n- [Logto Auth.js quickstart](https://docs.logto.io/quick-starts/next-auth)\n- [Integrate Logto in your application](https://docs.logto.io/integrate-logto/integrate-logto-into-your-application)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n    ```bash\n    https://example.com/api/auth/callback/logto\n    ```\n\n  </Code.Next>\n  <Code.Svelte>\n\n    ```bash\n    https://example.com/auth/callback/logto\n    ```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_LOGTO_ID\nAUTH_LOGTO_SECRET\nAUTH_LOGTO_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Logto from \"next-auth/providers/logto\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Logto],\n})\n```\n\n  </Code.Next>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Logto from \"@auth/sveltekit/providers/logto\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Logto],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Logto from \"@auth/express/providers/logto\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Logto] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/loops.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/loops.svg\" height=\"96\" />\n\n# Loops Provider\n\n## Overview\n\nThe Loops provider uses email to send \"magic links\" that contain URLs with verification tokens can be used to sign in.\n\nAdding support for signing in via email in addition to one or more OAuth services provides a way for users to sign in if they lose access to their OAuth account (e.g. if it is locked or deleted).\n\nThe Loops provider can be used in conjunction with (or instead of) one or more OAuth providers.\n\n## How it works\n\nOn initial sign in, a **Verification Token** is sent to the email address provided. By default this token is valid for 24 hours. If the Verification Token is used within that time (i.e. by clicking on the link in the email) an account is created for the user and they are signed in.\n\nIf someone provides the email address of an _existing account_ when signing in, an email is sent and they are signed into the account associated with that email address when they follow the link in the email.\n\n<Callout type=\"warning\">\n  The Loops provider can be used with both JSON Web Token and database managed\n  sessions, however **you must configure a database** to use it. It is not\n  possible to enable email sign in without using a database.\n</Callout>\n\n## Configuration\n\n### Add and Verify your Domain on Loops\n\nFirst, you'll need to have completed the steps covered in the ['Start here'](https://loops.so/docs/start-here) Loops documentation.\nThe main thing required is to [set up your domain records](https://loops.so/docs/start-here#1-set-up-your-domain-records).\n\n### Generate an API Key\n\nNext, you will have to generate an API key in the [Loops Dashboard](https://loops.so/api-keys). You can save this API key as the `AUTH_LOOPS_KEY` environment variable.\n\n```sh\nAUTH_LOOPS_KEY=abc\n```\n\n### Create a Transactional Email Template on Loops\n\nThe easiest way to achieve this is using the [Loops email editor](https://loops.so/docs/creating-emails/editor) to create a transactional email template.\nIf you're new to Loops, you can find rich documentation [here](https://loops.so/docs/transactional/guide).\n\n<br />\nCopy the Transactional ID value from the last page of the template creation\nprocess, and save this as the `AUTH_LOOPS_TRANSACTIONAL_ID` environment\nvariable. If you're following these steps, you should now have two environment\nvariables set up for Loops.\n\n```sh\nAUTH_LOOPS_KEY=abc\nAUTH_LOOPS_TRANSACTIONAL_ID=def\n```\n\n<Callout type=\"warning\">\n  When creating your email template, make sure to include the `url` variable in\n  the template. This is the URL that will sent to the user, allowing them to\n  signin.\n</Callout>\n\n<Code>\n<Code.Next>\n### Configure AuthJS with the Loops Provider\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Loops from \"next-auth/providers/loops\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: ..., // database adapter of your choosing\n  providers: [\n    Loops({\n      apiKey: process.env.AUTH_LOOPS_KEY,\n      transactionalId: process.env.AUTH_LOOPS_TRANSACTIONAL_ID,\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Svelte>\n### Configure AuthJS with the Loops Provider\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Loops from \"@auth/sveltekit/providers/loops\"\nimport { AUTH_LOOPS_KEY, AUTH_LOOPS_TRANSACTIONAL_ID } from \"@env/static/private\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: ..., // database adapter of your choosing\n  providers: [\n    Loops({\n      apiKey: AUTH_LOOPS_KEY,\n      transactionalId: AUTH_LOOPS_TRANSACTIONAL_ID,\n    }),\n  ],\n})\n```\n\n</Code.Svelte>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/mailchimp.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/mailchimp.svg\" height=\"64\" width=\"64\" />\n\n# Mailchip Provider\n\n## Resources\n\n- [Mailchimp OAuth documentation](https://admin.mailchimp.com/account/oauth2/client/)\n- [Mailchimp documentation: Access user data](https://mailchimp.com/developer/marketing/guides/access-user-data-oauth-2/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/mailchimp\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/mailchimp\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/mailchimp\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_MAILCHIMP_ID\nAUTH_MAILCHIMP_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport MailChimp from \"next-auth/providers/mailchimp\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [MailChimp],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport MailChimp from \"@auth/qwik/providers/mailchimp\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [MailChimp],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport MailChimp from \"@auth/sveltekit/providers/mailchimp\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [MailChimp],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport MailChimp from \"@auth/express/providers/mailchimp\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [MailChimp] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/mailgun.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/mailgun.svg\" height=\"64\" width=\"64\" />\n\n# Mailgun Provider\n\n## Resources\n\n- [Mailgun documentation](https://documentation.mailgun.com/docs/mailgun)\n\n## Overview\n\nThe Mailgun provider uses email to send \"magic links\" that contain URLs with verification tokens can be used to sign in.\n\nAdding support for signing in via email in addition to one or more OAuth services provides a way for users to sign in if they lose access to their OAuth account (e.g. if it is locked or deleted).\n\nThe Mailgun provider can be used in conjunction with (or instead of) one or more OAuth providers.\n\n### How it works\n\nOn initial sign in, a **Verification Token** is sent to the email address provided. By default this token is valid for 24 hours. If the Verification Token is used within that time (i.e. by clicking on the link in the email) an account is created for the user and they are signed in.\n\nIf someone provides the email address of an _existing account_ when signing in, an email is sent and they are signed into the account associated with that email address when they follow the link in the email.\n\n<Callout type=\"warning\">\n  The Mailgun provider can be used with both JSON Web Token and database managed\n  sessions, however **you must configure a database** to use it. It is not\n  possible to enable email sign in without using a database.\n</Callout>\n\n## Configuration\n\n1. First, you'll need to [add your domain](https://app.mailgun.com/mg/sending/domains) to your Mailgun account. This is required by Mailgun and this domain of the address you use in the `from` provider option.\n\n2. Next, you will have to generate an API key in the [Mailgun Settings](https://app.mailgun.com/settings/api_security/api_keys). You can save this API key as the `AUTH_MAILGUN_KEY` environment variable.\n\n```sh\nAUTH_MAILGUN_KEY=abc\n```\n\nIf you name your environment variable `AUTH_MAILGUN_KEY`, the provider will pick it up automatically and your Auth.js configuration object can be simpler. If you'd like to rename it to something else, however, you'll have to manually pass it into the provider in your Auth.js configuration.\n\n3. If you are using the EU Mailgun server, you will need to include `region: \"EU\"` in the provider options. If you are using the US Mailgun server you can remove this option.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Mailgun from \"next-auth/providers/mailgun\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: ...,\n  providers: [\n    Mailgun({\n      // If your environment variable is named differently than default\n      apiKey: process.env.AUTH_MAILGUN_KEY,\n      from: \"no-reply@company.com\",\n      region: \"EU\",  // Optional\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Mailgun from \"@auth/qwik/providers/mailgun\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Mailgun({\n        // If your environment variable is named differently than default\n        apiKey: import.meta.env.AUTH_MAILGUN_KEY,\n        from: \"no-reply@company.com\",\n        region: \"EU\", // Optional\n      }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Mailgun from \"@auth/sveltekit/providers/mailgun\"\nimport { env } from \"$env/dynamic/prviate\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: ...,\n  providers: [\n    Mailgun({\n      // If your environment variable is named differently than default\n      apiKey: env.AUTH_MAILGUN_KEY,\n      from: \"no-reply@company.com\",\n      region: \"EU\",  // Optional\n    }),\n  ],\n})\n```\n\n</Code.Svelte>\n</Code>\n\n4. Do not forget to setup one of the [database adapters](https://authjs.dev/getting-started/database) for storing the Email verification token.\n\n5. You can now start the sign-in process with an email address at `/api/auth/signin`.\n\nA user account (i.e. an entry in the `Users` table) will not be created for the user until the first time they verify their email address. If an email address is already associated with an account, the user will be signed in to that account when they click the link in magic link email and use up the verification token.\n\n## Customization\n\n### Email Body\n\nYou can fully customize the sign in email that is sent by passing a custom function as the `sendVerificationRequest` option to `Mailgun()`.\n\n```js {7} filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Mailgun from \"next-auth/providers/mailgun\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Mailgun({\n      server: process.env.EMAIL_SERVER,\n      from: process.env.EMAIL_FROM,\n      sendVerificationRequest({\n        identifier: email,\n        url,\n        provider: { server, from },\n      }) {\n        // your function\n      },\n    }),\n  ],\n})\n```\n\nAs an example, the following shows the source for our built-in `sendVerificationRequest()` method. Notice that we're rendering the HTML (`html()`) and making the network call (`fetch()`) to Mailgun to actually do the sending here in this method.\n\n```ts filename=\"./lib/authSendRequest.ts\" {13, 16}\nexport async function sendVerificationRequest(params) {\n  const { identifier: to, provider, url, theme } = params\n  const { host } = new URL(url)\n  const domain = provider.from.split(\"@\").at(1)\n\n  if (!domain) throw new Error(\"malformed Mailgun domain\")\n\n  const form = new FormData()\n  form.append(\"from\", `${provider.name} <${provider.from}>`)\n  form.append(\"to\", to)\n  form.append(\"subject\", `Sign in to ${host}`)\n  form.append(\"html\", html({ host, url, theme }))\n  form.append(\"text\", text({ host, url }))\n\n  const res = await fetch(`https://api.mailgun.net/v3/${domain}/messages`, {\n    method: \"POST\",\n    headers: {\n      Authorization: `Basic ${btoa(`api:${provider.apiKey}`)}`,\n    },\n    body: form,\n  })\n\n  if (!res.ok) throw new Error(\"Mailgun error: \" + (await res.text()))\n}\n\nfunction html(params: { url: string; host: string; theme: Theme }) {\n  const { url, host, theme } = params\n\n  const escapedHost = host.replace(/\\./g, \"&#8203;.\")\n\n  const brandColor = theme.brandColor || \"#346df1\"\n  const color = {\n    background: \"#f9f9f9\",\n    text: \"#444\",\n    mainBackground: \"#fff\",\n    buttonBackground: brandColor,\n    buttonBorder: brandColor,\n    buttonText: theme.buttonText || \"#fff\",\n  }\n\n  return `\n<body style=\"background: ${color.background};\">\n  <table width=\"100%\" border=\"0\" cellspacing=\"20\" cellpadding=\"0\"\n    style=\"background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;\">\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        Sign in to <strong>${escapedHost}</strong>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\" style=\"padding: 20px 0;\">\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n          <tr>\n            <td align=\"center\" style=\"border-radius: 5px;\" bgcolor=\"${color.buttonBackground}\"><a href=\"${url}\"\n                target=\"_blank\"\n                style=\"font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;\">Sign\n                in</a></td>\n          </tr>\n        </table>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        If you did not request this email you can safely ignore it.\n      </td>\n    </tr>\n  </table>\n</body>\n`\n}\n\n// Email Text body (fallback for email clients that don't render HTML, e.g. feature phones)\nfunction text({ url, host }: { url: string; host: string }) {\n  return `Sign in to ${host}\\n${url}\\n\\n`\n}\n```\n\n<Callout type=\"info\">\n  If you want to generate great looking emails with React that are compatible\n  with many email clients, check out [mjml](https://mjml.io) or\n  [react-email](https://react.email)\n</Callout>\n\n### Verification Tokens\n\nBy default, we are generating a random verification token. You can define a `generateVerificationToken` method in your provider options if you want to override it:\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Mailgun from \"next-auth/providers/mailgun\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Mailgun({\n      async generateVerificationToken() {\n        return crypto.randomUUID()\n      },\n    }),\n  ],\n})\n```\n\n### Normalizing Email Addresses\n\nBy default, Auth.js will normalize the email address. It treats the address as case-insensitive (which is technically not compliant to the [RFC 2821 spec](https://datatracker.ietf.org/doc/html/rfc2821), but in practice this causes more problems than it solves, i.e. when looking up users by e-mail from databases.) and also removes any secondary email address that may have been passed in as a comma-separated list. You can apply your own normalization via the `normalizeIdentifier` method on the `Mailgun` provider. The following example shows the default behavior:\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Mailgun from \"next-auth/providers/mailgun\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Mailgun({\n      normalizeIdentifier(identifier: string): string {\n        // Get the first two elements only,\n        // separated by `@` from user input.\n        let [local, domain] = identifier.toLowerCase().trim().split(\"@\")\n        // The part before \"@\" can contain a \",\"\n        // but we remove it on the domain part\n        domain = domain.split(\",\")[0]\n        return `${local}@${domain}`\n\n        // You can also throw an error, which will redirect the user\n        // to the sign-in page with error=EmailSignin in the URL\n        // if (identifier.split(\"@\").length > 2) {\n        //   throw new Error(\"Only one email allowed\")\n        // }\n      },\n    }),\n  ],\n})\n```\n\n<Callout type=\"warning\">\n  Always make sure this returns a single e-mail address, even if multiple ones\n  were passed in.\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/mailru.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/mailru.svg\" height=\"64\" width=\"128\" />\n\n# Mailru Provider\n\n## Resources\n\n- [Mailru OAuth documentation](https://o2.mail.ru/docs)\n- [Mailru app console](https://o2.mail.ru/app/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/mailru\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/mailru\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/mailru\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_MAILRU_ID\nAUTH_MAILRU_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport MailRu from \"next-auth/providers/mailru\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [MailRu],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport MailRu from \"@auth/qwik/providers/mailru\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [MailRu],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport MailRu from \"@auth/sveltekit/providers/mailru\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [MailRu],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport MailRu from \"@auth/express/providers/mailru\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [MailRu] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/mastodon.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/mastodon.svg\" height=\"64\" width=\"64\" />\n\n# Mastodon Provider\n\n## Resources\n\n- [Mastodon OAuth documentation](https://docs.joinmastodon.org/client/token/)\n- [Mastodon OAuth Configuration](https://mastodon.social/settings/applications)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/mastodon\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/mastodon\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/mastodon\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_MASTODON_ID\nAUTH_MASTODON_SECRET\nAUTH_MASTODON_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Mastodon from \"next-auth/providers/mastodon\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Mastodon],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Mastodon from \"@auth/qwik/providers/mastodon\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Mastodon],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Mastodon from \"@auth/sveltekit/providers/mastodon\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Mastodon],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Mastodon from \"@auth/express/providers/mastodon\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Mastodon] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Due to the way Mastodon is architected, you have to define the `issuer` to be the instance URL against which you want to authenticate\n"
  },
  {
    "path": "docs/pages/getting-started/providers/mattermost.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/mattermost.svg\" height=\"64\" width=\"64\" />\n\n# Mattermost Provider\n\n## Resources\n\n- [Mattermost OAuth documentation](https://example.com)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/mattermost\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/mattermost\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/mattermost\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_MATTERMOST_ID\nAUTH_MATTERMOST_SECRET\nAUTH_MATTERMOST_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Mattermost from \"next-auth/providers/mattermost\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Mattermost],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Mattermost from \"@auth/qwik/providers/mattermost\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Mattermost],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Mattermost from \"@auth/sveltekit/providers/mattermost\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Mattermost],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Mattermost from \"@auth/express/providers/mattermost\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Mattermost] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- To create your Mattermost OAuth2 app visit `http://<your Mattermost instance url>/<your team>/integrations/oauth2-apps`\n- The Mattermost provider requires the `issuer` option to be set. This is the base url of your Mattermost instance. e.g `https://my-cool-server.cloud.mattermost.com`\n"
  },
  {
    "path": "docs/pages/getting-started/providers/medium.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/medium.svg\" height=\"64\" width=\"64\" />\n\n# Medium Provider\n\n## Resources\n\n- [Medium OAuth documentation](https://example.com)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/medium\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/medium\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/medium\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_MEDIUM_ID\nAUTH_MEDIUM_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Medium from \"next-auth/providers/medium\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Medium],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Medium from \"@auth/qwik/providers/medium\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Medium],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Medium from \"@auth/sveltekit/providers/medium\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Medium],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Medium from \"@auth/express/providers/medium\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Medium] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Email address is not returned by the Medium API.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/microsoft-entra-id.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img\n  align=\"right\"\n  src=\"/img/providers/microsoft-entra-id.svg\"\n  height=\"64\"\n  width=\"64\"\n/>\n\n# Microsoft Entra ID\n\n<Callout>\n  Microsoft has renamed **Azure AD** to **Microsoft Entra ID**, more information\n  about the new name can be found\n  [here](https://learn.microsoft.com/en-us/entra/fundamentals/new-name).\n</Callout>\n\n## Resources\n\n- [Microsoft Entra OAuth documentation](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow)\n- [Microsoft Entra OAuth apps](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/microsoft-entra-id\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/microsoft-entra-id\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/microsoft-entra-id\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```bash\nhttps://example.com/auth/callback/microsoft-entra-id\n```\n\n  </Code.Express>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_MICROSOFT_ENTRA_ID_ID\nAUTH_MICROSOFT_ENTRA_ID_SECRET\nAUTH_MICROSOFT_ENTRA_ID_ISSUER\n```\n\n### Register Application\n\n1. Log in to the [Microsoft Entra admin center](https://entra.microsoft.com/).\n\n2. In the left sidebar, navigate to Identity --> Applications --> App\n   Registrations.\n\n3. Click on New registration.\n\n4. Give your application a name. This name will be displayed to the user when\n   they log in.\n\n5. Select the account types you want to allow to log in. The\n   `AUTH_MICROSOFT_ENTRA_ID_ISSUER` variable will be based on the selection you\n   make here.\n\n   - **Single tenant only** - Only allow users from your organization.<br />\n     `https://login.microsoftonline.com/<Directory (tenant) ID>/v2.0`\n\n   - **Multi-tenant** - Allow users from any organization.<br />\n     `https://login.microsoftonline.com/organizations/v2.0`\n\n   - **Multi-tenant + Personal** - Allow any Microsoft account (work, school,\n     personal).<br />\n     `https://login.microsoftonline.com/common/v2.0`\n\n   - **Personal Only** - Only allow personal Microsoft accounts.<br />\n     `https://login.microsoftonline.com/consumers/v2.0`\n\n6. Set the Redirect URI platform to `web` and the Callback URI for your\n   application. When developing you will set this to your local host\n   environment\n   (example `http://localhost:3000/api/auth/callback/microsoft-entra-id`).\n\n7. From the application overview page copy the **Application (client) ID** and\n   paste it in the `AUTH_MICROSOFT_ENTRA_ID_ID` variable.\n\n8. Navigate to Certificates & secrets and create a new client secret.\n\n9. Copy the secret value (this will be hidden when you leave this page) and\n   paste it in the `AUTH_MICROSOFT_ENTRA_ID_SECRET` variable.\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport MicrosoftEntraID from \"next-auth/providers/microsoft-entra-id\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    MicrosoftEntraID({\n      clientId: process.env.AUTH_MICROSOFT_ENTRA_ID_ID,\n      clientSecret: process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET,\n      issuer: process.env.AUTH_MICROSOFT_ENTRA_ID_ISSUER,\n    }),\n  ],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport MicrosoftEntraID from \"@auth/qwik/providers/microsoft-entra-id\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      MicrosoftEntraID({\n        clientId: import.meta.env.AUTH_MICROSOFT_ENTRA_ID_ID,\n        clientSecret: import.meta.env.AUTH_MICROSOFT_ENTRA_ID_SECRET,\n        issuer: import.meta.env.AUTH_MICROSOFT_ENTRA_ID_ISSUER,\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport MicrosoftEntraID from \"@auth/sveltekit/providers/microsoft-entra-id\"\nimport {\n  AUTH_MICROSOFT_ENTRA_ID_ID,\n  AUTH_MICROSOFT_ENTRA_ID_SECRET,\n  AUTH_MICROSOFT_ENTRA_ID_ISSUER,\n} from \"$env/static/private\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    MicrosoftEntraID({\n      clientId: AUTH_MICROSOFT_ENTRA_ID_ID,\n      clientSecret: AUTH_MICROSOFT_ENTRA_ID_SECRET,\n      issuer: AUTH_MICROSOFT_ENTRA_ID_ISSUER,\n    }),\n  ],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport MicrosoftEntraID from \"@auth/express/providers/microsoft-entra-id\"\n\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [\n      MicrosoftEntraID({\n        clientId: process.env.AUTH_MICROSOFT_ENTRA_ID_ID,\n        clientSecret: process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET,\n        issuer: process.env.AUTH_MICROSOFT_ENTRA_ID_ISSUER,\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Express>\n</Code>\n\n```env filename=\".env.local\"\nAUTH_MICROSOFT_ENTRA_ID_ID=\"<Application (client) ID>\"\nAUTH_MICROSOFT_ENTRA_ID_SECRET=\"<Client secret value>\"\nAUTH_MICROSOFT_ENTRA_ID_ISSUER=\"https://login.microsoftonline.com/<Directory (tenant) ID>/v2.0\"\n```\n\n## Notes\n\n- If the issuer paramater is not set it will default to\n  `https://login.microsoftonline.com/common/v2.0`.\n\n- Microsoft Entra returns the profile picture in an ArrayBuffer, instead of\n  just a URL to the image, so our provider converts it to a base64 encoded\n  image string and returns that instead. See\n  [Microsoft Graph profilePhoto](https://learn.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0&tabs=http#examples).\n  The default image size is 48x48 to avoid\n  [running out of space](https://next-auth.js.org/faq#json-web-tokens) in case\n  the session is saved as a JWT.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/naver.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/naver.svg\" height=\"64\" width=\"128\" />\n\n# Naver Provider\n\n## Resources\n\n- [Naver OAuth documentation](https://developers.naver.com/docs/login/overview/overview.md)\n- [Naver OAuth documentation 2](https://developers.naver.com/docs/login/api/api.md)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/naver\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/naver\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/naver\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_NAVER_ID\nAUTH_NAVER_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Naver from \"next-auth/providers/naver\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Naver],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Naver from \"@auth/qwik/providers/naver\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Naver],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Naver from \"@auth/sveltekit/providers/naver\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Naver],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Naver from \"@auth/express/providers/naver\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Naver] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/netlify.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/netlify.svg\" height=\"64\" width=\"64\" />\n\n# Netlify Provider\n\n## Resources\n\n- [Netlify OAuth blog](https://www.netlify.com/blog/2016/10/10/integrating-with-netlify-oauth2/)\n- [Netlify OAuth example](https://github.com/netlify/netlify-oauth-example/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/netlify\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/netlify\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/netlify\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_NETLIFY_ID\nAUTH_NETLIFY_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Netlify from \"next-auth/providers/netlify\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Netlify],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Netlify from \"@auth/qwik/providers/netlify\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Netlify],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Netlify from \"@auth/sveltekit/providers/netlify\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Netlify],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Netlify from \"@auth/express/providers/netlify\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Netlify] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/netsuite.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/netsuite.svg\" height=\"64\" width=\"64\" />\n\n# NetSuite\n\n<Callout type=\"warning\">To be released in `next-auth@5.0.0-beta.18`</Callout>\n\n## Resources\n\n- [NetSuite - Creating an Integration Record (OAuth 2.0)](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_157771733782.html#Related-Topics)\n- [NetSuite - Authorizing OAuth Requests](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps)\n- [NetSuite - Configure OAuth Roles](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_157771510070.html#Set-Up-OAuth-2.0-Roles)\n- [Learn more about NetSuite OAuth 2.0](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/chapter_157769826287.html#OAuth-2.0)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/netsuite\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/netsuite\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/netsuite\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```bash\nhttps://example.com/auth/callback/netsuite\n```\n\n  </Code.Express>\n</Code>\n\n> NetSuite does not support `http://` callback URLs. When testing locally, you can use a service like [ngrok](https://ngrok.com) to get a local `https` URL.\n\n### Environment Variables\n\n```\nAUTH_NETSUITE_ID\nAUTH_NETSUITE_SECRET\nAUTH_NETSUITE_ACCOUNT_ID\n```\n\n### Configuration\n\nBefore setting up the provider, you will need to ensure the following is setup.\n\n- [Create an Integration Record](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_157771733782.html#procedure_157838925981)\n  - Uncheck the TBA Auth Flow checkbox.\n  - Check OAuth 2.0 Auth Flow checkbox.\n  - Copy and paste the `Callback URL` below into the `Redirect URI` field.\n  - Then select the scope(s) you want to use.\n    - **REST Web Services** (`rest_webservices`) - Access to REST Web Services.\n    - **RESTlets**(`restlets`) - Access to RESTLets.\n    - **SuiteAnalytics Connect** (`suiteanalytics_connect`) - Access to SuiteAnalytics Connect.\n  - Add any policies you want to use.\n    - Application Logo (_Optional_) (Shown to users when they are asked to grant access to your application). - Consent Screen\n    - Application Terms of Use (_Optional_) - A PDF file that contains the terms of use for your application. - Consent Screen\n    - Application Privacy Policy (_Optional_) - A PDF file that contains the privacy policy for your application. - Consent Screen\n  - OAuth 2.0 Consent Policy Preference - This setting determines whether the user is asked to grant access to your application **every time** they sign in or only the **first time** they sign in or **never**.\n  - **Save** the Integration record.\n  - The Integration record will be used to generate the `clientId` and `clientSecret` for the provider. **Save the generated values for later**\n\n### Userinfo RESTLet Handler\n\nTo use this provider, you'll need to create a userinfo RESTlet in your NetSuite instance.\nOur `userinfo` URL needs to be a suitelet or RESTLet URL that gives us the\ninformation about the user. The best bet is to use the `N/runtime` module to\nget the basics first. - Here is an example of a RESTlet below. Be sure to\ndeploy and enable access to \"All Roles\".\n\nBe sure to deploy and use the **external** RESTLet url of any usage of the URIs.\n\n```js\n/**\n * @NApiVersion 2.1\n * @NScriptType Restlet\n */\ndefine([\"N/runtime\"],\n (runtime) => {\n/**\n * Defines the function that is executed when a GET request is sent to a RESTlet.\n * @param {Object} requestParams - Parameters from HTTP request URL; parameters passed as an Object (for all supported\n *     content types)\n * @returns {string | Object} HTTP response body; returns a string when request Content-Type is 'text/plain'; returns an\n *     Object when request Content-Type is 'application/json' or 'application/xml'\n * @since 2015.2\n */\n  const get = (requestParams) => {\n    let userObject = runtime.getCurrentUser();\n\n    try {\n      log.debug({ title: \"Payload received:\", details: requestParams });\n\n      const { id, name, role, location, email, contact } = userObject;\n\n      log.audit({ title: \"Current User Ran\", details: name });\n\n      let user = {\n        id,\n        name,\n        role,\n        location,\n        email,\n        contact,\n      };\n\n      log.debug({ title: \"Returning user\", details: user });\n\n      return JSON.stringify(user);\n    } catch (e) {\n      log.error({ title: \"Error grabbing current user:\", details: e });\n    }\n  };\n\n  return {\n    get,\n  };\n);\n```\n\nAbove is an example of returning the basic runtime information. Be sure to create a new script record and deployment record. When you save the deployment record, you will get the URLs for your RESTlet, which we will use as the `userinfo` URL.\n\n### Example Usage\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"@auth.ts\"\nimport NextAuth from \"next-auth\"\nimport NetSuite from \"next-auth/providers/netsuite\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    NetSuite({\n      clientId: process.env.AUTH_NETSUITE_ID,\n      clientSecret: process.env.AUTH_NETSUITE_SECRET,\n      issuer: process.env.AUTH_NETSUITE_ACCOUNT_ID, // EX: TSTDRV1234567 or 81555 for prod, and 1234567-SB1 for Sandbox accounts not \"_\" use \"-\".\n      // Returns the current user using the N/runtime module. This url can be a suitelet or RESTlet (Recommended)\n      // Using getCurrentUser(); So we match this schema returned from this RESTlet in the profile callback. (Required)\n      userinfo:\n        \"https://1234567.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=123&deploy=1\",\n      // Optional\n      prompt: \"login\", // Required if you want to force the user to login every time.\n      scope: \"restlets\", // Optional defaults to \"restlets rest_webservices\". Enter the scope(s) you want to use followed by spaces.\n    }),\n  ],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport NetSuite from \"@auth/qwik/providers/netsuite\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      NetSuite({\n        clientId: import.meta.env.AUTH_NETSUITE_ID,\n        clientSecret: import.meta.env.AUTH_NETSUITE_SECRET,\n        issuer: import.meta.env.AUTH_NETSUITE_ACCOUNT_ID, // EX: TSTDRV1234567 or 81555 for prod, and 1234567-SB1 for Sandbox accounts not \"_\" use \"-\".\n        // Returns the current user using the N/runtime module. This url can be a suitelet or RESTlet (Recommended)\n        // Using getCurrentUser(); So we match this schema returned from this RESTlet in the profile callback. (Required)\n        userinfo:\n          \"https://1234567.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=123&deploy=1\",\n        // Optional\n        prompt: \"login\", // Required if you want to force the user to login every time.\n        scope: \"restlets\", // Optional defaults to \"restlets rest_webservices\". Enter the scope(s) you want to use followed by spaces.\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport NetSuite from \"@auth/sveltekit/providers/netsuite\"\nimport { env } from \"$env/dynamic/private\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    NetSuite({\n      clientId: env.AUTH_NETSUITE_ID,\n      clientSecret: env.AUTH_NETSUITE_SECRET,\n      issuer: env.AUTH_NETSUITE_ACCOUNT_ID, // EX: TSTDRV1234567 or 81555 for prod, and 1234567-SB1 for Sandbox accounts not \"_\" use \"-\".\n      // Returns the current user using the N/runtime module. This url can be a suitelet or RESTlet (Recommended)\n      // Using getCurrentUser(); So we match this schema returned from this RESTlet in the profile callback. (Required)\n      userinfo:\n        \"https://1234567.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=123&deploy=1\",\n      // Optional\n      prompt: \"login\", // Required if you want to force the user to login every time.\n      scope: \"restlets\", // Optional defaults to \"restlets rest_webservices\". Enter the scope(s) you want to use followed by spaces.\n    }),\n  ],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport NetSuite from \"@auth/express/providers/netsuite\"\n\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [\n      NetSuite({\n        clientId: process.env.AUTH_NETSUITE_ID,\n        clientSecret: process.env.AUTH_NETSUITE_SECRET,\n        issuer: process.env.AUTH_NETSUITE_ACCOUNT_ID, // EX: TSTDRV1234567 or 81555 for prod, and 1234567-SB1 for Sandbox accounts not \"_\" use \"-\".\n        // Returns the current user using the N/runtime module. This url can be a suitelet or RESTlet (Recommended)\n        // Using getCurrentUser(); So we match this schema returned from this RESTlet in the profile callback. (Required)\n        userinfo:\n          \"https://1234567.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=123&deploy=1\",\n        // Optional\n        prompt: \"login\", // Required if you want to force the user to login every time.\n        scope: \"restlets\", // Optional defaults to \"restlets rest_webservices\". Enter the scope(s) you want to use followed by spaces.\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/nextcloud.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/nextcloud.svg\" height=\"64\" width=\"64\" />\n\n# Nextcloud Provider\n\n## Resources\n\n- [Nextcloud Documentation](https://docs.nextcloud.com/)\n- [Nextcloud OAuth 2](https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/oauth2.html)\n- [Nextcloud Clients and Client APIs](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/index.html)\n- [Nextcloud User provisioning API](https://docs.nextcloud.com/server/latest/admin_manual/configuration_user/user_provisioning_api.html)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/nextcloud\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/nextcloud\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/nextcloud\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_NEXTCLOUD_ID\nAUTH_NEXTCLOUD_SECRET\nAUTH_NEXTCLOUD_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts\nimport NextAuth from \"next-auth\"\nimport Nextcloud from \"next-auth/providers/nextcloud\"\n\nconst response = await NextAuth({\n  providers: [\n    Nextcloud({\n      clientId: process.env.AUTH_NEXTCLOUD_ID,\n      clientSecret: process.env.AUTH_NEXTCLOUD_SECRET,\n      issuer: process.env.AUTH_NEXTCLOUD_ISSUER,\n    }),\n  ],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Nextcloud from \"@auth/qwik/providers/nextcloud\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Nextcloud({\n        clientId: process.env.AUTH_NEXTCLOUD_ID,\n        clientSecret: process.env.AUTH_NEXTCLOUD_SECRET,\n        issuer: process.env.AUTH_NEXTCLOUD_ISSUER,\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Nextcloud from \"@auth/sveltekit/providers/nextcloud\"\nimport {\n  AUTH_NEXTCLOUD_ID,\n  AUTH_NEXTCLOUD_SECRET,\n  AUTH_NEXTCLOUD_ISSUER,\n} from \"$env/static/private\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    Nextcloud({\n      clientId: AUTH_NEXTCLOUD_ID,\n      clientSecret: AUTH_NEXTCLOUD_SECRET,\n      issuer: AUTH_NEXTCLOUD_ISSUER,\n    }),\n  ],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Nextcloud from \"@auth/express/providers/nextcloud\"\n\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [\n      Nextcloud({\n        clientId: AUTH_NEXTCLOUD_ID,\n        clientSecret: AUTH_NEXTCLOUD_SECRET,\n        issuer: AUTH_NEXTCLOUD_ISSUER,\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/nodemailer.mdx",
    "content": "import { Callout, Tabs } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/nodemailer.svg\" height=\"64\" width=\"96\" />\n\n# Nodemailer Provider\n\n## Overview\n\nThe Nodemailer provider uses email to send \"magic links\" that contain URLs with verification tokens can be used to sign in.\n\nAdding support for signing in via email in addition to one or more OAuth services provides a way for users to sign in if they lose access to their OAuth account (e.g. if it is locked or deleted).\n\nThe Nodemailer provider can be used in conjunction with (or instead of) one or more OAuth providers.\n\n### How it works\n\nOn initial sign in, a **Verification Token** is sent to the email address provided. By default this token is valid for 24 hours. If the Verification Token is used within that time (i.e. by clicking on the link in the email) an account is created for the user and they are signed in.\n\nIf someone provides the email address of an _existing account_ when signing in, an email is sent and they are signed into the account associated with that email address when they follow the link in the email.\n\n<Callout type=\"warning\">\n  The Nodemailer provider can be used with both JSON Web Token and database\n  managed sessions, however **you must configure a database** to use it. It is\n  not possible to enable email sign in without using a database.\n</Callout>\n\n## Configuration\n\n1. Auth.js does not include `nodemailer` as a dependency, so you'll need to install it yourself if you want to use the Nodemailer provider.\n\n```bash npm2yarn\nnpm install nodemailer\n```\n\n2. You will need an SMTP account; ideally for one of the [services known to work with `nodemailer`](https://community.nodemailer.com/2-0-0-beta/setup-smtp/well-known-services/). Nodemailer also works with [other transports](https://nodemailer.com/transports/), however if you want to use an HTTP based email service, we recommend using one of the other Auth.js providers designed for those, like [Resend](/getting-started/providers/resend) or [Sendgrid](/getting-started/providers/sendgrid).\n\n3. There are two ways to configure the SMTP server connection.\n\nYou can either use a connection string or a `nodemailer` configuration object.\n\n<Tabs items={['Connection string', 'Configuration object']}>\n  <Tabs.Tab>\n\n```bash filename=\".env\"\nEMAIL_SERVER=smtp://username:password@smtp.example.com:587\nEMAIL_FROM=noreply@example.com\n```\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Nodemailer from \"next-auth/providers/nodemailer\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: ...,\n  providers: [\n    Nodemailer({\n      server: process.env.EMAIL_SERVER,\n      from: process.env.EMAIL_FROM,\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Nodemailer from \"@auth/qwik/providers/nodemailer\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Nodemailer({\n        server: import.meta.env.EMAIL_SERVER,\n        from: import.meta.env.EMAIL_FROM,\n      }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Nodemailer from \"@auth/sveltekit/providers/nodemailer\"\nimport { env } from \"$env/dynamic/prviate\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: ...,\n  providers: [\n    Nodemailer({\n      server: env.EMAIL_SERVER,\n      from: env.EMAIL_FROM,\n    }),\n  ],\n})\n```\n\n```ts filename=\"src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n\n  </Tabs.Tab>\n  <Tabs.Tab>\n\n```bash filename=\".env\"\nEMAIL_SERVER_USER=username\nEMAIL_SERVER_PASSWORD=password\nEMAIL_SERVER_HOST=smtp.example.com\nEMAIL_SERVER_PORT=587\nEMAIL_FROM=noreply@example.com\n```\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Nodemailer from \"next-auth/providers/nodemailer\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: ...,\n  providers: [\n    Nodemailer({\n      server: {\n        host: process.env.EMAIL_SERVER_HOST,\n        port: process.env.EMAIL_SERVER_PORT,\n        auth: {\n          user: process.env.EMAIL_SERVER_USER,\n          pass: process.env.EMAIL_SERVER_PASSWORD,\n        },\n      },\n      from: process.env.EMAIL_FROM,\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Nodemailer from \"@auth/sveltekit/providers/nodemailer\"\nimport { env } from \"$env/dynamic/prviate\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: ...,\n  providers: [\n    Nodemailer({\n      server: {\n        host: env.EMAIL_SERVER_HOST,\n        port: env.EMAIL_SERVER_PORT,\n        auth: {\n          user: env.EMAIL_SERVER_USER,\n          pass: env.EMAIL_SERVER_PASSWORD,\n        },\n      },\n      from: env.EMAIL_FROM,\n    }),\n  ],\n})\n```\n\n</Code.Svelte>\n</Code>\n\n  </Tabs.Tab>\n</Tabs>\n\n4. Do not forget to setup one of the database [adapters](https://authjs.dev/reference/core/adapters) for storing the Email verification token.\n\n5. You can now start the sign process with an email address at `/api/auth/signin`.\n\nA user account (i.e. an entry in the `Users` table) will not be created for the user until the first time they verify their email address. If an email address is already associated with an account, the user will be signed in to that account when they use the link in the email.\n\n## Customization\n\n### Email Body\n\nYou can fully customize the sign in email that is sent by passing a custom function as the `sendVerificationRequest` option to `Nodemailer()`.\n\n```ts {7} filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Nodemailer from \"next-auth/providers/nodemailer\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Nodemailer({\n      server: process.env.EMAIL_SERVER,\n      from: process.env.EMAIL_FROM,\n      sendVerificationRequest({\n        identifier: email,\n        url,\n        provider: { server, from },\n      }) {\n        // your function\n      },\n    }),\n  ],\n})\n```\n\nAs an example, the following shows the source for our built-in `sendVerificationRequest()` method. Notice that we're rendering the HTML (`html()`) and making the network call (`transport.sendMail()`) to the email provider to actually do the sending here in this method.\n\n```ts {8, 13}\nimport { createTransport } from \"nodemailer\"\n\nexport async function sendVerificationRequest(params) {\n  const { identifier, url, provider, theme } = params\n  const { host } = new URL(url)\n  // NOTE: You are not required to use `nodemailer`, use whatever you want.\n  const transport = createTransport(provider.server)\n  const result = await transport.sendMail({\n    to: identifier,\n    from: provider.from,\n    subject: `Sign in to ${host}`,\n    text: text({ url, host }),\n    html: html({ url, host, theme }),\n  })\n  const failed = result.rejected.concat(result.pending).filter(Boolean)\n  if (failed.length) {\n    throw new Error(`Email(s) (${failed.join(\", \")}) could not be sent`)\n  }\n}\n\nfunction html(params: { url: string; host: string; theme: Theme }) {\n  const { url, host, theme } = params\n\n  const escapedHost = host.replace(/\\./g, \"&#8203;.\")\n\n  const brandColor = theme.brandColor || \"#346df1\"\n  const color = {\n    background: \"#f9f9f9\",\n    text: \"#444\",\n    mainBackground: \"#fff\",\n    buttonBackground: brandColor,\n    buttonBorder: brandColor,\n    buttonText: theme.buttonText || \"#fff\",\n  }\n\n  return `\n<body style=\"background: ${color.background};\">\n  <table width=\"100%\" border=\"0\" cellspacing=\"20\" cellpadding=\"0\"\n    style=\"background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;\">\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        Sign in to <strong>${escapedHost}</strong>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\" style=\"padding: 20px 0;\">\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n          <tr>\n            <td align=\"center\" style=\"border-radius: 5px;\" bgcolor=\"${color.buttonBackground}\"><a href=\"${url}\"\n                target=\"_blank\"\n                style=\"font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;\">Sign\n                in</a></td>\n          </tr>\n        </table>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        If you did not request this email you can safely ignore it.\n      </td>\n    </tr>\n  </table>\n</body>\n`\n}\n\n// Email Text body (fallback for email clients that don't render HTML, e.g. feature phones)\nfunction text({ url, host }: { url: string; host: string }) {\n  return `Sign in to ${host}\\n${url}\\n\\n`\n}\n```\n\n<Callout type=\"info\">\n  If you want to generate great looking emails with React that are compatible\n  with many email clients, check out [mjml](https://mjml.io) or\n  [react-email](https://react.email)\n</Callout>\n\n### Verification Tokens\n\nBy default, we are generating a random verification token. You can define a `generateVerificationToken` method in your provider options if you want to override it:\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Nodemailer from \"next-auth/providers/nodemailer\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Nodemailer({\n      async generateVerificationToken() {\n        return crypto.randomUUID()\n      },\n    }),\n  ],\n})\n```\n\n### Normalizing Email Addresses\n\nBy default, Auth.js will normalize the email address. It treats the address as case-insensitive (which is technically not compliant to the [RFC 2821 spec](https://datatracker.ietf.org/doc/html/rfc2821), but in practice this causes more problems than it solves, i.e. when looking up users by e-mail from databases.) and also removes any secondary email address that may have been passed in as a comma-separated list. You can apply your own normalization via the `normalizeIdentifier` method on the `Nodemailer` provider. The following example shows the default behavior:\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Nodemailer from \"next-auth/providers/nodemailer\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Nodemailer({\n      normalizeIdentifier(identifier: string): string {\n        // Get the first two elements only,\n        // separated by `@` from user input.\n        let [local, domain] = identifier.toLowerCase().trim().split(\"@\")\n        // The part before \"@\" can contain a \",\"\n        // but we remove it on the domain part\n        domain = domain.split(\",\")[0]\n        return `${local}@${domain}`\n\n        // You can also throw an error, which will redirect the user\n        // to the sign-in page with error=EmailSignin in the URL\n        // if (identifier.split(\"@\").length > 2) {\n        //   throw new Error(\"Only one email allowed\")\n        // }\n      },\n    }),\n  ],\n})\n```\n\n<Callout type=\"warning\">\n  Always make sure this returns a single e-mail address, even if multiple ones\n  were passed in.\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/notion.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/notion.svg\" height=\"64\" width=\"64\" />\n\n# Notion Provider\n\n## Resources\n\n- [Notion documentation](https://developers.notion.com/docs)\n- [Notion Authorization documentation](https://developers.notion.com/docs/authorization)\n- [Notion Integrations](https://www.notion.so/my-integrations)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/notion\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/notion\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/notion\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_NOTION_ID\nAUTH_NOTION_SECRET\nAUTH_NOTION_REDIRECT_URI\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Notion from \"next-auth/providers/notion\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Notion({\n      clientId: process.env.AUTH_NOTION_ID,\n      clientSecret: process.env.AUTH_NOTION_SECRET,\n      redirectUri: process.env.AUTH_NOTION_REDIRECT_URI,\n    }),\n  ],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Notion from \"@auth/qwik/providers/notion\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Notion({\n        clientId: import.meta.env.AUTH_NOTION_ID,\n        clientSecret: import.meta.env.AUTH_NOTION_SECRET,\n        redirectUri: import.meta.env.AUTH_NOTION_REDIRECT_URI,\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Notion from \"@auth/sveltekit/providers/notion\"\nimport {\n  AUTH_NOTION_ID,\n  AUTH_NOTION_SECRET,\n  AUTH_NOTION_REDIRECT_URI,\n} from \"$env/static/private\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    Notion({\n      clientId: AUTH_NOTION_ID,\n      clientSecret: AUTH_NOTION_SECRET,\n      redirectUri: AUTH_NOTION_REDIRECT_URI,\n    }),\n  ],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Notion from \"@auth/express/providers/notion\"\n\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [\n      Notion({\n        clientId: process.env.AUTH_NOTION_ID,\n        clientSecret: process.env.AUTH_NOTION_SECRET,\n        redirectUri: process.env.AUTH_NOTION_REDIRECT_URI,\n      }),\n    ],\n  })\n)\n```\n\n  </Code.Express>\n</Code>\n\n## Notes\n\n- You need to select \"Public Integration\" on the configuration page to get an `oauth_id` and `oauth_secret`. Private integrations do not provide these details.\n- You must provide a `clientId` and `clientSecret` to use this provider, as-well as a redirect URI (due to this being required by Notion endpoint to fetch tokens).\n"
  },
  {
    "path": "docs/pages/getting-started/providers/okta.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/okta.svg\" height=\"64\" width=\"64\" />\n\n# Okta Provider\n\n## Resources\n\n- [Okta OAuth documentation](https://developer.okta.com/docs/reference/api/oidc)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/okta\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/okta\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/okta\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_OKTA_ID\nAUTH_OKTA_SECRET\nAUTH_OKTA_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Okta from \"next-auth/providers/okta\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Okta],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Okta from \"@auth/qwik/providers/okta\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Okta],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Okta from \"@auth/sveltekit/providers/okta\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Okta],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Okta from \"@auth/express/providers/okta\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Okta] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/onelogin.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/onelogin.svg\" height=\"64\" width=\"64\" />\n\n# OneLogin Provider\n\n## Resources\n\n- [OneLogin OAuth documentation](https://example.com)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/onelogin\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/onelogin\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/onelogin\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_ONELOGIN_ID\nAUTH_ONELOGIN_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport OneLogin from \"next-auth/providers/onelogin\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [OneLogin],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport OneLogin from \"@auth/qwik/providers/onelogin\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [OneLogin],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport OneLogin from \"@auth/sveltekit/providers/onelogin\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [OneLogin],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport OneLogin from \"@auth/express/providers/onelogin\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [OneLogin] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/osso.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/osso.svg\" height=\"64\" width=\"64\" />\n\n# Osso Provider\n\n## Resources\n\n- [Osso Project](https://github.com/enterprise-oss/osso)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/osso\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/osso\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/osso\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_OSSO_ID\nAUTH_OSSO_SECRET\nAUTH_OSSO_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Osso from \"next-auth/providers/osso\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Osso],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Osso from \"@auth/qwik/providers/osso\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Osso],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Osso from \"@auth/sveltekit/providers/osso\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Osso],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Osso from \"@auth/express/providers/osso\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Osso] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- You can configure your OAuth Clients on your Osso Admin UI, i.e. https://yourInstance.com/admin/config - you'll need to get a Client ID and Secret and allow-list your redirect URIs.\n- SAML - SSO differs a bit from OAuth, for every tenant who wants to sign in to your application using SAML, you and your customer need to perform a multi-step configuration in Osso's Admin UI and the admin dashboard of the tenant's Identity Provider. Osso provides documentation for providers like Okta and Osso, cloud-based IDPs who also offer a developer account that's useful for testing. Osso also provides a Mock IDP that you can use for testing without needing to sign up for an Identity Provider service.\n- `issuer` should be the fully qualified domain – e.g. `demo.ossoapp.com`\n"
  },
  {
    "path": "docs/pages/getting-started/providers/osu.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/osu.svg\" height=\"64\" width=\"64\" />\n\n# Osu Provider\n\n## Resources\n\n- [osu! OAuth documentation](https://osu.ppy.sh/docs/index.html#authentication)\n- [osu! app console](https://osu.ppy.sh/home/account/edit#new-oauth-application)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/osu\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/osu\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/osu\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_OSU_ID\nAUTH_OSU_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Osu from \"next-auth/providers/osu\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Osu],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Osu from \"@auth/qwik/providers/osu\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Osu],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Osu from \"@auth/sveltekit/providers/osu\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Osu],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Osu from \"@auth/express/providers/osu\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Osu] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- osu! does not provide a user email.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/passage.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/passage.svg\" height=\"64\" width=\"64\" />\n\n# Passage Provider\n\n## Resources\n\n- [Passage OIDC documentation](https://docs.passage.id/hosted-login/oidc-client-configuration)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/passage\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/passage\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/passage\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_PASSAGE_ID\nAUTH_PASSAGE_SECRET\nAUTH_PASSAGE_ISSUER\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Passage from \"next-auth/providers/passage\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Passage],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Passage from \"@auth/qwik/providers/passage\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Passage],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Passage from \"@auth/sveltekit/providers/passage\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Passage],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Passage from \"@auth/express/providers/passage\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Passage] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/passkey.mdx",
    "content": "import { Callout, Steps } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\nimport { Accordion, Accordions } from \"@/components/Accordion\"\n\n<img align=\"right\" src=\"/img/providers/passkey.svg\" width=\"128\" height=\"128\" />\n\n# Passkey\n\n## Setup\n\n<Callout type=\"warning\">\n  The WebAuthn / Passkeys provider is experimental and not yet recommended for\n  production use.\n</Callout>\n\nThe Passkeys provider **requires a database adapter** as well as a new table in that database. Please see the docs page for your adapter for the respective migration details.\n\nPasskeys are currently supported in the following adapters / framework packages.\n\n| Package                   | Minimum Version | Link                                        |\n| ------------------------- | --------------- | ------------------------------------------- |\n| `next-auth`               | `5.0.0-beta.17` |                                             |\n| `@auth/sveltekit`         | `1.0.2`         |                                             |\n| `@auth/prisma-adapter`    | `1.3.3`         | [Docs](/getting-started/adapters/prisma)    |\n| `@auth/unstorage-adapter` | `2.1.0`         | [Docs](/getting-started/adapters/unstorage) |\n| `@auth/drizzle-adapter`   | `1.1.1`         | [Docs](/getting-started/adapters/drizzle)   |\n\n<Steps>\n### Install peer dependencies\n\n```bash npm2yarn\nnpm install @simplewebauthn/browser@9.0.1 @simplewebauthn/server@9.0.3\n```\n\nThe `@simplewebauthn/browser` peer dependency is only required for custom signin pages. If you're using the Auth.js default pages, you can skip installing that peer dependency.\n\n### Database Setup\n\nThe Passkeys provider requires an additional table called `Authenticator`. Passkeys are now supported in multiple adapters, please see their respective docs pages for more detailed migration steps. We'll use Prisma as an example going forward here, but there is also a raw SQL migration included below.\n\n<Accordions>\n<Accordion title=\"Prisma Schema\">\n\n```prisma {18, 54-66}\ndatasource db {\n  provider = \"sqlite\"\n  url      = \"file:./dev.db\"\n}\n\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\nmodel User {\n  id            String    @id @default(cuid())\n  name          String?\n  email         String    @unique\n  emailVerified DateTime?\n  image         String?\n  accounts      Account[]\n  sessions      Session[]\n  Authenticator Authenticator[]\n}\n\nmodel Account {\n  userId            String\n  type              String\n  provider          String\n  providerAccountId String\n  refresh_token     String?\n  access_token      String?\n  expires_at        Int?\n  token_type        String?\n  scope             String?\n  id_token          String?\n  session_state     String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@id([provider, providerAccountId])\n}\n\nmodel Session {\n  sessionToken String   @unique\n  userId       String\n  expires      DateTime\n  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n\nmodel VerificationToken {\n  identifier String\n  token      String   @unique\n  expires    DateTime\n\n  @@id([identifier, token])\n}\n\nmodel Authenticator {\n  id                   String  @id @default(cuid())\n  credentialID         String  @unique\n  userId               String\n  providerAccountId    String\n  credentialPublicKey  String\n  counter              Int\n  credentialDeviceType String\n  credentialBackedUp   Boolean\n  transports           String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n```\n\n  </Accordion>\n  <Accordion title=\"SQL Migration\">\n\nThis migration works for **PostgreSQL** and **SQLite**.\n\n```sql filename=\"./migration/add-webauthn-authenticator-table-postgres.sql\"\n-- CreateTable\nCREATE TABLE \"Authenticator\" (\n    \"id\" TEXT NOT NULL PRIMARY KEY,\n    \"credentialID\" TEXT NOT NULL,\n    \"userId\" TEXT NOT NULL,\n    \"providerAccountId\" TEXT NOT NULL,\n    \"credentialPublicKey\" TEXT NOT NULL,\n    \"counter\" INTEGER NOT NULL,\n    \"credentialDeviceType\" TEXT NOT NULL,\n    \"credentialBackedUp\" BOOLEAN NOT NULL,\n    \"transports\" TEXT,\n    CONSTRAINT \"Authenticator_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES \"User\" (\"id\") ON DELETE CASCADE ON UPDATE CASCADE\n);\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"Authenticator_credentialID_key\" ON \"Authenticator\"(\"credentialID\");\n```\n\nThis migration works for **MySQL** / **MariaDB**.\n\n```sql filename=\"./migration/add-webauthn-authenticator-table-mysql.sql\"\n-- CreateTable\nCREATE TABLE Authenticator (\n    id varchar(255) NOT NULL PRIMARY KEY,\n    credentialID TEXT NOT NULL,\n    userId varchar(255) NOT NULL,\n    providerAccountId TEXT NOT NULL,\n    credentialPublicKey TEXT NOT NULL,\n    counter INTEGER NOT NULL,\n    credentialDeviceType TEXT NOT NULL,\n    credentialBackedUp BOOLEAN NOT NULL,\n    transports TEXT,\n    CONSTRAINT Authenticator_userId_fkey FOREIGN KEY (userId) REFERENCES User (id) ON DELETE CASCADE ON UPDATE CASCADE\n);\n\n-- CreateIndex\nCREATE UNIQUE INDEX Authenticator_credentialID_key ON Authenticator(credentialID);\n```\n\n  </Accordion>\n  </Accordions>\n\n#### Edge Compatibility\n\nIf you're using `next-auth` with Next.js and a proxy (or middleware in older versions), you should ensure that your database client of choice is \"edge compatible\" when using Next.js versions before 16. In Next.js 16+, `proxy.ts` runs on the Node.js runtime, so edge compatibility is no longer a concern. For older versions, check out our [edge compatibility](/guides/edge-compatibility) guide for more details. There is also Prisma specific information in the [Prisma adapter docs](/getting-started/adapters/prisma#edge-compatibility).\n\n### Update Auth.js Configuration\n\nAdd the `Passkey` provider to your configuration and make sure you're using a compatible database adapter. You'll also need to explicitly enable the experimental WebAuthn feature.\n\n```ts filename=\"./auth.ts\" {10}\nimport Passkey from \"next-auth/providers/passkey\"\nimport { PrismaAdapter } from \"@auth/prisma-adapter\"\nimport { PrismaClient } from \"@prisma/client\"\n\nconst prisma = new PrismaClient()\n\nexport default {\n  adapter: PrismaAdapter(prisma),\n  providers: [Passkey],\n  experimental: { enableWebAuthn: true },\n}\n```\n\nIf you're using the built-in Auth.js pages, then you are good to go now! Navigating to your `/signin` route should include a \"Signin with Passkeys\" button now.\n\n### Custom Pages\n\nIf you're building a custom signin page, you can leverage the `next-auth/webauthn` `signIn` function to initiate both WebAuthn registration and authentication. Remember, when using the WebAuthn `signIn` function, you'll also need the `@simplewebauth/browser` peer dependency installed.\n\n```ts filename=\"app/login/page.tsx\" {4} /webauthn/\n\"use client\"\n\nimport { useSession } from \"next-auth/react\"\nimport { signIn } from \"next-auth/webauthn\"\n\nexport default function Login() {\n  const { data: session, update, status } = useSession()\n\n  return (\n    <div>\n      {status === \"authenticated\" ? (\n        <button onClick={() => signIn(\"passkey\", { action: \"register\" })}>\n          Register new Passkey\n        </button>\n      ) : status === \"unauthenticated\" ? (\n        <button onClick={() => signIn(\"passkey\")}>Sign in with Passkey</button>\n      ) : null}\n    </div>\n  )\n}\n```\n\n</Steps>\n\n## Options\n\nYou can find all of the Passkeys provider options under the [API reference](/reference/core/providers/webauthn#webauthnconfig).\n"
  },
  {
    "path": "docs/pages/getting-started/providers/patreon.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/patreon.svg\" height=\"64\" width=\"64\" />\n\n# Patreon Provider\n\n## Resources\n\n- [Patreon OAuth documentation](https://docs.patreon.com/#apiv2-oauth)\n- [Patreon Platform](https://www.patreon.com/portal/registration/register-clients)\n- [ApiV2 Scopes](https://docs.patreon.com/#scopes)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/patreon\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/patreon\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/patreon\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_PATREON_ID\nAUTH_PATREON_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Patreon from \"next-auth/providers/patreon\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Patreon],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Patreon from \"@auth/qwik/providers/patreon\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Patreon],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Patreon from \"@auth/sveltekit/providers/patreon\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Patreon],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Patreon from \"@auth/express/providers/patreon\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Patreon] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/pinterest.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/pinterest.svg\" height=\"64\" width=\"64\" />\n\n# Pinterest Provider\n\n## Resources\n\n- [Pinterest OAuth documentation](https://developers.pinterest.com/docs/getting-started/authentication/)\n- [Pinterest app console](https://developers.pinterest.com/apps/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/pinterest\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/pinterest\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/pinterest\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_PINTEREST_ID\nAUTH_PINTEREST_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Pinterest from \"next-auth/providers/pinterest\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Pinterest],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Pinterest from \"@auth/qwik/providers/pinterest\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Pinterest],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Pinterest from \"@auth/sveltekit/providers/pinterest\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Pinterest],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Pinterest from \"@auth/express/providers/pinterest\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Pinterest] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- To use in production, make sure the app has standard API access and not trial access\n"
  },
  {
    "path": "docs/pages/getting-started/providers/pipedrive.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/pipedrive.svg\" height=\"64\" width=\"128\" />\n\n# Pipedrive Provider\n\n## Resources\n\n- [Pipedrive OAuth documentation](https://pipedrive.readme.io/docs/marketplace-oauth-authorization)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/pipedrive\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/pipedrive\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/pipedrive\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_PIPEDRIVE_ID\nAUTH_PIPEDRIVE_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport PipeDrive from \"next-auth/providers/pipedrive\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [PipeDrive],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport PipeDrive from \"@auth/qwik/providers/pipedrive\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [PipeDrive],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport PipeDrive from \"@auth/sveltekit/providers/pipedrive\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [PipeDrive],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport PipeDrive from \"@auth/express/providers/pipedrive\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [PipeDrive] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/postmark.mdx",
    "content": "import { Callout, Tabs } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/postmark.svg\" height=\"64\" width=\"96\" />\n\n# Postmark Provider\n\n## Overview\n\nThe Postmark provider uses email to send \"magic links\" that contain URLs with verification tokens can be used to sign in.\n\nAdding support for signing in via email in addition to one or more OAuth services provides a way for users to sign in if they lose access to their OAuth account (e.g. if it is locked or deleted).\n\nThe Postmark provider can be used in conjunction with (or instead of) one or more OAuth providers.\n\n### How it works\n\nOn initial sign in, a **Verification Token** is sent to the email address provided. By default this token is valid for 24 hours. If the Verification Token is used within that time (i.e. by clicking on the link in the email) an account is created for the user and they are signed in.\n\nIf someone provides the email address of an _existing account_ when signing in, an email is sent and they are signed into the account associated with that email address when they follow the link in the email.\n\n<Callout type=\"warning\">\n  The Postmark provider can be used with both JSON Web Token and database\n  managed sessions, however **you must configure a database** to use it. It is\n  not possible to enable email sign in without using a database.\n</Callout>\n\n## Configuration\n\n1. First, you'll need to [add your domain](https://account.postmarkapp.com/sign_up) to your Postmark account. This is required by Postmark and this domain of the address you use in the `from` provider option.\n\n2. Next, you will have to generate an API key in the [Postmark Dashboard](https://account.postmarkapp.com/api_tokens). You can save this API key as the `AUTH_POSTMARK_KEY` environment variable.\n\n```sh\nAUTH_POSTMARK_KEY=abc\n```\n\nIf you name your environment variable `AUTH_POSTMARK_KEY`, the provider will pick it up automatically and your Auth.js configuration object can be simpler. If you'd like to rename it to something else, however, you'll have to manually pass it into the provider in your Auth.js configuration.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Postmark from \"next-auth/providers/postmark\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: ...,\n  providers: [\n    Postmark({\n      // If your environment variable is named differently than default\n      apiKey: AUTH_POSTMARK_KEY,\n      from: \"no-reply@company.com\"\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Postmark from \"@auth/qwik/providers/postmark\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Postmark({\n        // If your environment variable is named differently than default\n        apiKey: import.meta.env.AUTH_POSTMARK_KEY,\n        from: \"no-reply@company.com\",\n      }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Postmark from \"@auth/sveltekit/providers/postmark\"\nimport { env } from \"$env/dynamic/prviate\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: ...,\n  providers: [\n    Postmark({\n      // If your environment variable is named differently than default\n      apiKey: env.AUTH_POSTMARK_KEY,\n      from: \"no-reply@company.com\",\n    }),\n  ],\n})\n```\n\n</Code.Svelte>\n</Code>\n\n4. Do not forget to setup one of the [database adapters](https://authjs.dev/getting-started/database) for storing the Email verification token.\n\n5. You can now start the sign-in process with an email address at `/api/auth/signin`.\n\nA user account (i.e. an entry in the `Users` table) will not be created for the user until the first time they verify their email address. If an email address is already associated with an account, the user will be signed in to that account when they click the link in magic link email and use up the verification token.\n\n## Customization\n\n### Email Body\n\nYou can fully customize the sign in email that is sent by passing a custom function as the `sendVerificationRequest` option to `Postmark()`.\n\n```js {7} filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Postmark from \"next-auth/providers/postmark\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Postmark({\n      server: process.env.EMAIL_SERVER,\n      from: process.env.EMAIL_FROM,\n      sendVerificationRequest({\n        identifier: email,\n        url,\n        provider: { server, from },\n      }) {\n        // your function\n      },\n    }),\n  ],\n})\n```\n\nAs an example, the following shows the source for our built-in `sendVerificationRequest()` method. Notice that we're rendering the HTML (`html()`) and making the network call (`fetch()`) to Postmark to actually do the sending here in this method.\n\n```ts filename=\"./lib/authSendRequest.ts\" {4, 14}\nexport async function sendVerificationRequest(params) {\n  const { identifier: to, provider, url, theme } = params\n  const { host } = new URL(url)\n  const res = await fetch(\"https://api.postmark.com/emails\", {\n    method: \"POST\",\n    headers: {\n      Authorization: `Bearer ${provider.apiKey}`,\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify({\n      from: provider.from,\n      to,\n      subject: `Sign in to ${host}`,\n      html: html({ url, host, theme }),\n      text: text({ url, host }),\n    }),\n  })\n\n  if (!res.ok)\n    throw new Error(\"Postmark error: \" + JSON.stringify(await res.json()))\n}\n\nfunction html(params: { url: string; host: string; theme: Theme }) {\n  const { url, host, theme } = params\n\n  const escapedHost = host.replace(/\\./g, \"&#8203;.\")\n\n  const brandColor = theme.brandColor || \"#346df1\"\n  const color = {\n    background: \"#f9f9f9\",\n    text: \"#444\",\n    mainBackground: \"#fff\",\n    buttonBackground: brandColor,\n    buttonBorder: brandColor,\n    buttonText: theme.buttonText || \"#fff\",\n  }\n\n  return `\n<body style=\"background: ${color.background};\">\n  <table width=\"100%\" border=\"0\" cellspacing=\"20\" cellpadding=\"0\"\n    style=\"background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;\">\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        Sign in to <strong>${escapedHost}</strong>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\" style=\"padding: 20px 0;\">\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n          <tr>\n            <td align=\"center\" style=\"border-radius: 5px;\" bgcolor=\"${color.buttonBackground}\"><a href=\"${url}\"\n                target=\"_blank\"\n                style=\"font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;\">Sign\n                in</a></td>\n          </tr>\n        </table>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        If you did not request this email you can safely ignore it.\n      </td>\n    </tr>\n  </table>\n</body>\n`\n}\n\n// Email Text body (fallback for email clients that don't render HTML, e.g. feature phones)\nfunction text({ url, host }: { url: string; host: string }) {\n  return `Sign in to ${host}\\n${url}\\n\\n`\n}\n```\n\n<Callout type=\"info\">\n  If you want to generate great looking emails with React that are compatible\n  with many email clients, check out [mjml](https://mjml.io) or\n  [react-email](https://react.email)\n</Callout>\n\n### Verification Tokens\n\nBy default, we are generating a random verification token. You can define a `generateVerificationToken` method in your provider options if you want to override it:\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Postmark from \"next-auth/providers/postmark\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Postmark({\n      async generateVerificationToken() {\n        return crypto.randomUUID()\n      },\n    }),\n  ],\n})\n```\n\n### Normalizing Email Addresses\n\nBy default, Auth.js will normalize the email address. It treats the address as case-insensitive (which is technically not compliant to the [RFC 2821 spec](https://datatracker.ietf.org/doc/html/rfc2821), but in practice this causes more problems than it solves, i.e. when looking up users by e-mail from databases.) and also removes any secondary email address that may have been passed in as a comma-separated list. You can apply your own normalization via the `normalizeIdentifier` method on the `Postmark` provider. The following example shows the default behavior:\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Postmark from \"next-auth/providers/postmark\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Postmark({\n      normalizeIdentifier(identifier: string): string {\n        // Get the first two elements only,\n        // separated by `@` from user input.\n        let [local, domain] = identifier.toLowerCase().trim().split(\"@\")\n        // The part before \"@\" can contain a \",\"\n        // but we remove it on the domain part\n        domain = domain.split(\",\")[0]\n        return `${local}@${domain}`\n\n        // You can also throw an error, which will redirect the user\n        // to the sign-in page with error=EmailSignin in the URL\n        // if (identifier.split(\"@\").length > 2) {\n        //   throw new Error(\"Only one email allowed\")\n        // }\n      },\n    }),\n  ],\n})\n```\n\n<Callout type=\"warning\">\n  Always make sure this returns a single e-mail address, even if multiple ones\n  were passed in.\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/reddit.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/reddit.svg\" height=\"64\" width=\"64\" />\n\n# Reddit Provider\n\n## Resources\n\n- [Reddit API documentation](https://www.reddit.com/dev/api/)\n- [Reddit app console](https://www.reddit.com/prefs/apps/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/reddit\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/reddit\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/reddit\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_REDDIT_ID\nAUTH_REDDIT_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Reddit from \"next-auth/providers/reddit\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Reddit],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Reddit from \"@auth/qwik/providers/reddit\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Reddit],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Reddit from \"@auth/sveltekit/providers/reddit\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Reddit],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Reddit from \"@auth/express/providers/reddit\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Reddit] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Reddit requires authorization every time you go through their page.\n- Allows one callback URL per Client ID / Client Secret.\n- This Provider template only has a one hour access token to it and only has the \"identity\" scope. If you want to get a refresh token as well you must set these authorization params:\n\n```ts filename=\"./auth.ts\"\nexport const { handlers, auth, signin, signout } = NextAuth({\n  providers: [\n    RedditProvider({\n      clientId: process.env.REDDIT_CLIENT_ID,\n      clientSecret: process.env.REDDIT_CLIENT_SECRET,\n      authorization: {\n        params: {\n          duration: \"permanent\",\n        },\n      },\n    }),\n  ],\n})\n```\n"
  },
  {
    "path": "docs/pages/getting-started/providers/resend.mdx",
    "content": "import { Callout, Tabs } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img\n  align=\"right\"\n  src=\"/img/providers/resend.svg\"\n  className=\"dark:bg-[currentColor]\"\n  height=\"64\"\n  width=\"96\"\n/>\n\n# Resend Provider\n\n## Overview\n\nThe Resend provider uses email to send \"magic links\" that contain URLs with verification tokens can be used to sign in.\n\nAdding support for signing in via email in addition to one or more OAuth services provides a way for users to sign in if they lose access to their OAuth account (e.g. if it is locked or deleted).\n\nThe Resend provider can be used in conjunction with (or instead of) one or more OAuth providers.\n\n### How it works\n\nOn initial sign in, a **Verification Token** is sent to the email address provided. By default this token is valid for 24 hours. If the Verification Token is used within that time (i.e. by clicking on the link in the email) an account is created for the user and they are signed in.\n\nIf someone provides the email address of an _existing account_ when signing in, an email is sent and they are signed into the account associated with that email address when they follow the link in the email.\n\n<Callout type=\"warning\">\n  The Resend provider can be used with both JSON Web Token and database managed\n  sessions, however **you must configure a database** to use it. It is not\n  possible to enable email sign in without using a database.\n</Callout>\n\n## Configuration\n\n1. First, you'll need to [add your domain](https://resend.com/domains) to your Resend account. This is required by Resend and this domain of the address you use in the `from` provider option.\n\n2. Next, you will have to generate an API key in the [Resend Dashboard](https://resend.com/api-keys). You can save this API key as the `AUTH_RESEND_KEY` environment variable.\n\n```sh\nAUTH_RESEND_KEY=abc\n```\n\nIf you name your environment variable `AUTH_RESEND_KEY`, the provider will pick it up automatically and your Auth.js configuration object can be simpler. If you'd like to rename it to something else, however, you'll have to manually pass it into the provider in your Auth.js configuration.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Resend from \"next-auth/providers/resend\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: ...,\n  providers: [\n    Resend({\n      // If your environment variable is named differently than default\n      apiKey: AUTH_RESEND_KEY,\n      from: \"no-reply@company.com\"\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Resend from \"@auth/qwik/providers/resend\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Resend({\n        // If your environment variable is named differently than default\n        apiKey: import.meta.env.AUTH_RESEND_KEY,\n        from: \"no-reply@company.com\",\n      }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Resend from \"@auth/sveltekit/providers/resend\"\nimport { env } from \"$env/dynamic/prviate\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: ...,\n  providers: [\n    Resend({\n      // If your environment variable is named differently than default\n      apiKey: env.AUTH_RESEND_KEY,\n      from: \"no-reply@company.com\",\n    }),\n  ],\n})\n```\n\n</Code.Svelte>\n</Code>\n\n4. Do not forget to setup one of the [database adapters](https://authjs.dev/getting-started/database) for storing the Email verification token.\n\n5. You can now start the sign-in process with an email address at `/api/auth/signin`.\n\nA user account (i.e. an entry in the `Users` table) will not be created for the user until the first time they verify their email address. If an email address is already associated with an account, the user will be signed in to that account when they click the link in magic link email and use up the verification token.\n\n## Customization\n\n### Email Body\n\nYou can fully customize the sign in email that is sent by passing a custom function as the `sendVerificationRequest` option to `Resend()`.\n\n```js {7} filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Resend from \"next-auth/providers/resend\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Resend({\n      server: process.env.EMAIL_SERVER,\n      from: process.env.EMAIL_FROM,\n      sendVerificationRequest({\n        identifier: email,\n        url,\n        provider: { server, from },\n      }) {\n        // your function\n      },\n    }),\n  ],\n})\n```\n\nAs an example, the following shows the source for our built-in `sendVerificationRequest()` method. Notice that we're rendering the HTML (`html()`) and making the network call (`fetch()`) to Resend to actually do the sending here in this method.\n\n```ts filename=\"./lib/authSendRequest.ts\" {4, 14}\nexport async function sendVerificationRequest(params) {\n  const { identifier: to, provider, url, theme } = params\n  const { host } = new URL(url)\n  const res = await fetch(\"https://api.resend.com/emails\", {\n    method: \"POST\",\n    headers: {\n      Authorization: `Bearer ${provider.apiKey}`,\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify({\n      from: provider.from,\n      to,\n      subject: `Sign in to ${host}`,\n      html: html({ url, host, theme }),\n      text: text({ url, host }),\n    }),\n  })\n\n  if (!res.ok)\n    throw new Error(\"Resend error: \" + JSON.stringify(await res.json()))\n}\n\nfunction html(params: { url: string; host: string; theme: Theme }) {\n  const { url, host, theme } = params\n\n  const escapedHost = host.replace(/\\./g, \"&#8203;.\")\n\n  const brandColor = theme.brandColor || \"#346df1\"\n  const color = {\n    background: \"#f9f9f9\",\n    text: \"#444\",\n    mainBackground: \"#fff\",\n    buttonBackground: brandColor,\n    buttonBorder: brandColor,\n    buttonText: theme.buttonText || \"#fff\",\n  }\n\n  return `\n<body style=\"background: ${color.background};\">\n  <table width=\"100%\" border=\"0\" cellspacing=\"20\" cellpadding=\"0\"\n    style=\"background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;\">\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        Sign in to <strong>${escapedHost}</strong>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\" style=\"padding: 20px 0;\">\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n          <tr>\n            <td align=\"center\" style=\"border-radius: 5px;\" bgcolor=\"${color.buttonBackground}\"><a href=\"${url}\"\n                target=\"_blank\"\n                style=\"font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;\">Sign\n                in</a></td>\n          </tr>\n        </table>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        If you did not request this email you can safely ignore it.\n      </td>\n    </tr>\n  </table>\n</body>\n`\n}\n\n// Email Text body (fallback for email clients that don't render HTML, e.g. feature phones)\nfunction text({ url, host }: { url: string; host: string }) {\n  return `Sign in to ${host}\\n${url}\\n\\n`\n}\n```\n\n<Callout type=\"info\">\n  If you want to generate great looking emails with React that are compatible\n  with many email clients, check out [mjml](https://mjml.io) or\n  [react-email](https://react.email)\n</Callout>\n\n### Verification Tokens\n\nBy default, we are generating a random verification token. You can define a `generateVerificationToken` method in your provider options if you want to override it:\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Resend from \"next-auth/providers/resend\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Resend({\n      async generateVerificationToken() {\n        return crypto.randomUUID()\n      },\n    }),\n  ],\n})\n```\n\n### Normalizing Email Addresses\n\nBy default, Auth.js will normalize the email address. It treats the address as case-insensitive (which is technically not compliant to the [RFC 2821 spec](https://datatracker.ietf.org/doc/html/rfc2821), but in practice this causes more problems than it solves, i.e. when looking up users by e-mail from databases.) and also removes any secondary email address that may have been passed in as a comma-separated list. You can apply your own normalization via the `normalizeIdentifier` method on the `Resend` provider. The following example shows the default behavior:\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Resend from \"next-auth/providers/resend\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Resend({\n      normalizeIdentifier(identifier: string): string {\n        // Get the first two elements only,\n        // separated by `@` from user input.\n        let [local, domain] = identifier.toLowerCase().trim().split(\"@\")\n        // The part before \"@\" can contain a \",\"\n        // but we remove it on the domain part\n        domain = domain.split(\",\")[0]\n        return `${local}@${domain}`\n\n        // You can also throw an error, which will redirect the user\n        // to the sign-in page with error=EmailSignin in the URL\n        // if (identifier.split(\"@\").length > 2) {\n        //   throw new Error(\"Only one email allowed\")\n        // }\n      },\n    }),\n  ],\n})\n```\n\n<Callout type=\"warning\">\n  Always make sure this returns a single e-mail address, even if multiple ones\n  were passed in.\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/sailpoint.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/sailpoint.svg\" height=\"64\" width=\"64\" />\n\n# SailPoint ISC Provider\n\nSailPoint Identity Secure Cloud (ISC) is an enterprise SaaS platform for identity and security. In order to use this OAuth integration, you will need an ISC tenant. If you're a SailPoint customer or partner, please talk to your SailPoint account manager for more details. If you are a developer, check out the [SailPoint Developer Community](https://developer.sailpoint.com/discuss/).\n\n<Callout>\n  This provider is not shipped with any of the Auth.js packages because it is an\n  enterprise provider for which we cannot obtain a tenant to test and ensure\n  compatibility. That being said, we'd like to make providers like these\n  available to our users, so we will share a copy and paste version of the\n  provider on respective docs pages like this. The provider configuration below\n  is provided as-is and has been submitted by a community member with access to\n  a SailPoint tenant.\n</Callout>\n\n## Resources\n\n- [SailPoint Identity Secure Cloud Authentication](https://developer.sailpoint.com/docs/api/authentication#choose-authorization-grant-flow)\n- [Managing API Keys and Personal Access Tokens](https://documentation.sailpoint.com/saas/help/common/api_keys.html?h=oauth+client#creating-an-api-key)\n- [SailPoint Developer Community](https://developer.sailpoint.com/discuss/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/sailpoint\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/sailpoint\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/sailpoint\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```bash\nhttps://example.com/auth/callback/sailpoint\n```\n\n  </Code.Express>\n</Code>\n\n### Create OAuth Client\n\nFirst, you'll need to create a client in your SailPoint admin console in order to get your `clientId` and `clientSecret`. You can follow this [guide](https://documentation.sailpoint.com/saas/help/common/api_keys.html?h=oauth+client#creating-an-api-key), or follow the main steps below.\n\n1. Create an OAuth Client () with grant types: `AUTHORIZATION_TOKEN` and `REFRESH_TOKEN`.\n2. Set the redirect URL to match your callback URL, based on the example above.\n3. Finally, select the scopes `sp:scope:all`.\n4. Click \"**Create**\" and note down the generated `clientId` and `clientSecret`.\n\n### Environment Variables\n\n```sh\nAUTH_SAILPOINT_ID=\nAUTH_SAILPOINT_SECRET=\nAUTH_SAILPOINT_BASE_URL=https://{tenant}.identitynow.com\nAUTH_SAILPOINT_BASE_API_URL=https://{tenant}.api.identitynow.com\n```\n\n### Configuration\n\nUnlike other Auth.js providers, this cannot be imported from the package (see the note at the top of this page for more details). However, you can copy and paste the following object into your `providers` array to enable this provider.\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    {\n      id: \"sailpoint\",\n      name: \"SailPoint\",\n      type: \"oauth\",\n      clientId: process.env.AUTH_SAILPOINT_ID!,\n      clientSecret: process.env.AUTH_SAILPOINT_SECRET!,\n      authorization: {\n        url: `${process.env.AUTH_SAILPOINT_BASE_URL!}/oauth/authorize`,\n        params: { scope: \"sp:scopes:all\" },\n      },\n      token: `${process.env.AUTH_SAILPOINT_BASE_API_URL!}/oauth/token`,\n      userinfo: `${process.env.AUTH_SAILPOINT_BASE_API_URL!}/oauth/userinfo`,\n      profile(profile) {\n        return {\n          id: profile.id,\n          email: profile.email,\n          name: profile.uid,\n          image: null,\n        }\n      },\n      style: { brandColor: \"#011E69\", logo: \"sailpoint.svg\" },\n    },\n  ],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      {\n        id: \"sailpoint\",\n        name: \"SailPoint\",\n        type: \"oauth\",\n        clientId: import.meta.env.AUTH_SAILPOINT_ID!,\n        clientSecret: import.meta.env.AUTH_SAILPOINT_SECRET!,\n        authorization: {\n          url: `${import.meta.env.AUTH_SAILPOINT_BASE_URL!}/oauth/authorize`,\n          params: { scope: \"sp:scopes:all\" },\n        },\n        token: `${import.meta.env.AUTH_SAILPOINT_BASE_API_URL!}/oauth/token`,\n        userinfo: `${import.meta.env.AUTH_SAILPOINT_BASE_API_URL!}/oauth/userinfo`,\n        profile(profile) {\n          return {\n            id: profile.id,\n            email: profile.email,\n            name: profile.uid,\n            image: null,\n          }\n        },\n        style: { brandColor: \"#011E69\", logo: \"sailpoint.svg\" },\n      },\n    ],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport { env } from \"$env/dynamic/prviate\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [\n    {\n      id: \"sailpoint\",\n      name: \"SailPoint\",\n      type: \"oauth\",\n      clientId: env.AUTH_SAILPOINT_ID!,\n      clientSecret: env.AUTH_SAILPOINT_SECRET!,\n      authorization: {\n        url: `${env.AUTH_SAILPOINT_BASE_URL!}/oauth/authorize`,\n        params: { scope: \"sp:scopes:all\" },\n      },\n      token: `${env.AUTH_SAILPOINT_BASE_API_URL!}/oauth/token`,\n      userinfo: `${env.AUTH_SAILPOINT_BASE_API_URL!}/oauth/userinfo`,\n      profile(profile) {\n        return {\n          id: profile.id,\n          email: profile.email,\n          name: profile.uid,\n          image: null,\n        }\n      },\n      style: { brandColor: \"#011E69\", logo: \"sailpoint.svg\" },\n    },\n  ],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\n\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [\n      {\n        id: \"sailpoint\",\n        name: \"SailPoint\",\n        type: \"oauth\",\n        clientId: process.env.AUTH_SAILPOINT_ID!,\n        clientSecret: process.env.AUTH_SAILPOINT_SECRET!,\n        authorization: {\n          url: `${process.env.AUTH_SAILPOINT_BASE_URL!}/oauth/authorize`,\n          params: { scope: \"sp:scopes:all\" },\n        },\n        token: `${process.env.AUTH_SAILPOINT_BASE_API_URL!}/oauth/token`,\n        userinfo: `${process.env.AUTH_SAILPOINT_BASE_API_URL!}/oauth/userinfo`,\n        profile(profile) {\n          return {\n            id: profile.id,\n            email: profile.email,\n            name: profile.uid,\n            image: null,\n          }\n        },\n        style: { brandColor: \"#011E69\", logo: \"sailpoint.svg\" },\n      },\n    ],\n  })\n)\n```\n\n  </Code.Express>\n</Code>\n\n### Profile\n\nThe SailPoint `userprofile` endpoint will return more fields, but by default the [User table](/getting-started/database#models) only supports `id`, `name`, `email`, and `image`. Therefore, if you'd like to use any of the following fields and you're using a database adapter with Auth.js, make sure you modify the `User` table schema in whichever adapter and database you're using. Then you can additionally return any of these fields from the `profile` callback above.\n\nThe available fields from the SailPoint `userprofile` endpoint response include the following.\n\n```ts\ntype SailPointProfile = {\n  tenant: string\n  id: string\n  uid: string\n  email: string\n  phone: string\n  workPhone: string\n  firstname: string\n  lastname: string\n  capabilities: string\n  displayName: string\n  name: string\n}\n```\n"
  },
  {
    "path": "docs/pages/getting-started/providers/salesforce.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/salesforce.svg\" height=\"64\" width=\"64\" />\n\n# Salesforce Provider\n\n## Resources\n\n- [Salesforce OAuth documentation](https://help.salesforce.com/articleView?id=remoteaccess_authenticate.htm&type=5)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/salesforce\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/salesforce\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/salesforce\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_SALESFORCE_ID\nAUTH_SALESFORCE_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Salesforce from \"next-auth/providers/salesforce\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Salesforce],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Salesforce from \"@auth/qwik/providers/salesforce\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Salesforce],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Salesforce from \"@auth/sveltekit/providers/salesforce\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Salesforce],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Salesforce from \"@auth/express/providers/salesforce\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Salesforce] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/sendgrid.mdx",
    "content": "import { Callout, Tabs } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/sendgrid.svg\" height=\"64\" width=\"96\" />\n\n# Sendgrid Provider\n\n## Overview\n\nThe Sendgrid provider uses email to send \"magic links\" that contain URLs with verification tokens can be used to sign in.\n\nAdding support for signing in via email in addition to one or more OAuth services provides a way for users to sign in if they lose access to their OAuth account (e.g. if it is locked or deleted).\n\nThe Sendgrid provider can be used in conjunction with (or instead of) one or more OAuth providers.\n\n### How it works\n\nOn initial sign in, a **Verification Token** is sent to the email address provided. By default this token is valid for 24 hours. If the Verification Token is used within that time (i.e. by clicking on the link in the email) an account is created for the user and they are signed in.\n\nIf someone provides the email address of an _existing account_ when signing in, an email is sent and they are signed into the account associated with that email address when they follow the link in the email.\n\n<Callout type=\"warning\">\n  The Sendgrid provider can be used with both JSON Web Token and database\n  managed sessions, however **you must configure a database** to use it. It is\n  not possible to enable email sign in without using a database.\n</Callout>\n\n## Configuration\n\n1. First, you'll need to [add your domain](https://sendgrid.com/domains) to your Sendgrid account. This is required by Sendgrid and this domain of the address you use in the `from` provider option.\n\n2. Next, you will have to generate an API key in the [Sendgrid Dashboard](https://sendgrid.com/api-keys). You can save this API key as the `AUTH_SENDGRID_KEY` environment variable.\n\n```sh\nAUTH_SENDGRID_KEY=abc\n```\n\nIf you name your environment variable `AUTH_SENDGRID_KEY`, the provider will pick it up automatically and your Auth.js configuration object can be simpler. If you'd like to rename it to something else, however, you'll have to manually pass it into the provider in your Auth.js configuration.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Sendgrid from \"next-auth/providers/sendgrid\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: ...,\n  providers: [\n    Sendgrid({\n      // If your environment variable is named differently than default\n      apiKey: COMPANY_AUTH_SENDGRID_API_KEY,\n      from: \"no-reply@company.com\"\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Sendgrid from \"@auth/qwik/providers/sendgrid\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Sendgrid({\n        // If your environment variable is named differently than default\n        apiKey: import.meta.env.COMPANY_AUTH_SENDGRID_API_KEY,\n        from: \"no-reply@company.com\",\n      }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Sendgrid from \"@auth/sveltekit/providers/sendgrid\"\nimport { env } from \"$env/dynamic/prviate\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  adapter: ...,\n  providers: [\n    Sendgrid({\n      // If your environment variable is named differently than default\n      apiKey: env.COMPANY_AUTH_SENDGRID_API_KEY,\n      from: \"no-reply@company.com\",\n    }),\n  ],\n})\n```\n\n</Code.Svelte>\n</Code>\n\n4. Do not forget to setup one of the [database adapters](https://authjs.dev/getting-started/database) for storing the Email verification token.\n\n5. You can now start the sign-in process with an email address at `/api/auth/signin`.\n\nA user account (i.e. an entry in the `Users` table) will not be created for the user until the first time they verify their email address. If an email address is already associated with an account, the user will be signed in to that account when they click the link in magic link email and use up the verification token.\n\n## Customization\n\n### Email Body\n\nYou can fully customize the sign in email that is sent by passing a custom function as the `sendVerificationRequest` option to `Sendgrid()`.\n\n```js {7} filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Sendgrid from \"next-auth/providers/sendgrid\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Sendgrid({\n      server: process.env.EMAIL_SERVER,\n      from: process.env.EMAIL_FROM,\n      sendVerificationRequest({\n        identifier: email,\n        url,\n        provider: { server, from },\n      }) {\n        // your function\n      },\n    }),\n  ],\n})\n```\n\nAs an example, the following shows the source for our built-in `sendVerificationRequest()` method. Notice that we're rendering the HTML (`html()`) and making the network call (`fetch()`) to Sendgrid to actually do the sending here in this method.\n\n```ts {4, 16} filename=\"./lib/authSendRequest.ts\"\nexport async function sendVerificationRequest(params) {\n  const { identifier: to, provider, url, theme } = params\n  const { host } = new URL(url)\n  const res = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n    method: \"POST\",\n    headers: {\n      Authorization: `Bearer ${provider.apiKey}`,\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify({\n      personalizations: [{ to: [{ email: to }] }],\n      from: { email: provider.from },\n      subject: `Sign in to ${host}`,\n      content: [\n        { type: \"text/plain\", value: text({ url, host }) },\n        { type: \"text/html\", value: html({ url, host, theme }) },\n      ],\n    }),\n  })\n\n  if (!res.ok) throw new Error(\"Sendgrid error: \" + (await res.text()))\n}\n\nfunction html(params: { url: string; host: string; theme: Theme }) {\n  const { url, host, theme } = params\n\n  const escapedHost = host.replace(/\\./g, \"&#8203;.\")\n\n  const brandColor = theme.brandColor || \"#346df1\"\n  const color = {\n    background: \"#f9f9f9\",\n    text: \"#444\",\n    mainBackground: \"#fff\",\n    buttonBackground: brandColor,\n    buttonBorder: brandColor,\n    buttonText: theme.buttonText || \"#fff\",\n  }\n\n  return `\n<body style=\"background: ${color.background};\">\n  <table width=\"100%\" border=\"0\" cellspacing=\"20\" cellpadding=\"0\"\n    style=\"background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;\">\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        Sign in to <strong>${escapedHost}</strong>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\" style=\"padding: 20px 0;\">\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n          <tr>\n            <td align=\"center\" style=\"border-radius: 5px;\" bgcolor=\"${color.buttonBackground}\"><a href=\"${url}\"\n                target=\"_blank\"\n                style=\"font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;\">Sign\n                in</a></td>\n          </tr>\n        </table>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        If you did not request this email you can safely ignore it.\n      </td>\n    </tr>\n  </table>\n</body>\n`\n}\n\n// Email Text body (fallback for email clients that don't render HTML, e.g. feature phones)\nfunction text({ url, host }: { url: string; host: string }) {\n  return `Sign in to ${host}\\n${url}\\n\\n`\n}\n```\n\n<Callout type=\"info\">\n  If you want to generate great looking emails with React that are compatible\n  with many email clients, check out [mjml](https://mjml.io) or\n  [react-email](https://react.email)\n</Callout>\n\n### Verification Tokens\n\nBy default, we are generating a random verification token. You can define a `generateVerificationToken` method in your provider options if you want to override it:\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Sendgrid from \"next-auth/providers/sendgrid\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Sendgrid({\n      async generateVerificationToken() {\n        return crypto.randomUUID()\n      },\n    }),\n  ],\n})\n```\n\n### Normalizing Email Addresses\n\nBy default, Auth.js will normalize the email address. It treats the address as case-insensitive (which is technically not compliant to the [RFC 2821 spec](https://datatracker.ietf.org/doc/html/rfc2821), but in practice this causes more problems than it solves, i.e. when looking up users by e-mail from databases.) and also removes any secondary email address that may have been passed in as a comma-separated list. You can apply your own normalization via the `normalizeIdentifier` method on the `Sendgrid` provider. The following example shows the default behavior:\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Sendgrid from \"next-auth/providers/sendgrid\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    Sendgrid({\n      normalizeIdentifier(identifier: string): string {\n        // Get the first two elements only,\n        // separated by `@` from user input.\n        let [local, domain] = identifier.toLowerCase().trim().split(\"@\")\n        // The part before \"@\" can contain a \",\"\n        // but we remove it on the domain part\n        domain = domain.split(\",\")[0]\n        return `${local}@${domain}`\n\n        // You can also throw an error, which will redirect the user\n        // to the sign-in page with error=EmailSignin in the URL\n        // if (identifier.split(\"@\").length > 2) {\n        //   throw new Error(\"Only one email allowed\")\n        // }\n      },\n    }),\n  ],\n})\n```\n\n<Callout type=\"warning\">\n  Always make sure this returns a single e-mail address, even if multiple ones\n  were passed in.\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/simplelogin.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img\n  align=\"right\"\n  src=\"/img/providers/simplelogin.svg\"\n  height=\"64\"\n  width=\"64\"\n/>\n\n# SimpleLogin Provider\n\n## Resources\n\n- [Sign in with SimpleLogin](https://simplelogin.io/developer/)\n- [SimpleLogin OAuth documentation](https://simplelogin.io/docs/siwsl/intro/)\n- [SimpleLogin OAuth Configuration](https://app.simplelogin.io/developer)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/simplelogin\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/simplelogin\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/simplelogin\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_SIMPLELOGIN_ID\nAUTH_SIMPLELOGIN_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"@/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport SimpleLogin from \"next-auth/providers/simplelogin\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [SimpleLogin],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport SimpleLogin from \"@auth/qwik/providers/simplelogin\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [SimpleLogin],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport SimpleLogin from \"@auth/sveltekit/providers/simplelogin\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [SimpleLogin],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport SimpleLogin from \"@auth/express/providers/simplelogin\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [SimpleLogin] }))\n```\n\n  </Code.Express>\n</Code>\n\n## Notes\n\n### Authorized Redirect URIs\n\nThe \"Authorized redirect URIs\" used must include your full domain and end in the callback path. By default, SimpleLogin whitelists all `http[s]://localhost:*` address to facilitate local development. For example;\n\n- For production: `https://{YOUR_DOMAIN}/api/auth/callback/simplelogin`\n- For development: By default **localhost** is whitelisted.\n\n**Authorized Redirect URIs** must be **HTTPS** for security reason (except for `localhost`).\n"
  },
  {
    "path": "docs/pages/getting-started/providers/slack.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/slack.svg\" height=\"64\" width=\"64\" />\n\n# Slack Provider\n\n## Resources\n\n- [Slack Authentication documentation](https://api.slack.com/authentication)\n- [Sign-in with Slack](https://api.slack.com/docs/sign-in-with-slack)\n- [Slack app console](https://api.slack.com/apps)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/slack\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/slack\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/slack\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_SLACK_ID\nAUTH_SLACK_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Slack from \"next-auth/providers/slack\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Slack],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Slack from \"@auth/qwik/providers/slack\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Slack],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Slack from \"@auth/sveltekit/providers/slack\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Slack],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Slack from \"@auth/express/providers/slack\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Slack] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Slack requires that the redirect URL of your app uses https, even for local development. An easy workaround for this is using a service like [ngrok](https://ngrok.com/) that creates a secure tunnel to your app, using https. Remember to set the url as `AUTH_URL` as well.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/spotify.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/spotify.svg\" height=\"64\" width=\"64\" />\n\n# Spotify Provider\n\n## Resources\n\n- [Spotify OAuth documentation](https://developer.spotify.com/documentation/general/guides/authorization-guide)\n- [Spotify app console](https://developer.spotify.com/dashboard/applications)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/spotify\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/spotify\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/spotify\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_SPOTIFY_ID\nAUTH_SPOTIFY_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Spotify from \"next-auth/providers/spotify\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Spotify],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Spotify from \"@auth/qwik/providers/spotify\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Spotify],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Spotify from \"@auth/sveltekit/providers/spotify\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Spotify],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Spotify from \"@auth/express/providers/spotify\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Spotify] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/strava.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/strava.svg\" height=\"64\" width=\"64\" />\n\n# Strava Provider\n\n## Resources\n\n- [Strava API documentation](http://developers.strava.com/docs/reference/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/strava\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/strava\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/strava\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_STRAVA_ID\nAUTH_STRAVA_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Strava from \"next-auth/providers/strava\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Strava],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Strava from \"@auth/qwik/providers/strava\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Strava],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Strava from \"@auth/sveltekit/providers/strava\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Strava],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Strava from \"@auth/express/providers/strava\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Strava] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/threads.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/threads.svg\" height=\"64\" width=\"64\" />\n\n# Threads Provider\n\n## Resources\n\n- [Threads OAuth documentation](https://developers.facebook.com/docs/threads)\n- [Threads OAuth apps](https://developers.facebook.com/apps/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/threads\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/threads\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/threads\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_THREADS_ID\nAUTH_THREADS_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Threads from \"next-auth/providers/threads\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Threads],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Threads from \"@auth/qwik/providers/threads\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Threads],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Threads from \"@auth/sveltekit/providers/threads\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Threads],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Threads from \"@auth/express/providers/threads\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Threads] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Email address is not returned by the Threads API.\n- Threads requires a callback URL to be configured in your Facebook app and Facebook requires you to use **https** even for localhost. In order to do that, you either need to [add an SSL to your localhost](https://www.freecodecamp.org/news/how-to-get-https-working-on-your-local-development-environment-in-5-minutes-7af615770eec/) or use a proxy such as [ngrok](https://ngrok.com/docs).\n"
  },
  {
    "path": "docs/pages/getting-started/providers/tiktok.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/tiktok.svg\" height=\"64\" width=\"64\" />\n\n# TikTok Provider\n\n## Resources\n\n- [TikTok app console](https://developers.tiktok.com/)\n- [TikTok login kit documentation](https://developers.tiktok.com/doc/login-kit-web/)\n- [Available Scopes](https://developers.tiktok.com/doc/tiktok-api-scopes/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/tiktok\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/tiktok\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/tiktok\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_TIKTOK_ID\nAUTH_TIKTOK_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport TikTok from \"next-auth/providers/tiktok\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [TikTok],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport TikTok from \"@auth/qwik/providers/tiktok\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [TikTok],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport TikTok from \"@auth/sveltekit/providers/tiktok\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [TikTok],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport TikTok from \"@auth/express/providers/tiktok\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [TikTok] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Production applications cannot use localhost URLs to sign in with TikTok. You need add the domain and Callback/Redirect url's to your TikTok app and have them review and approved by the TikTok Team.\n- Email address is not supported by TikTok.\n- Client_ID will be the Client Key in the TikTok Application\n"
  },
  {
    "path": "docs/pages/getting-started/providers/todoist.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/todoist.svg\" height=\"64\" width=\"64\" />\n\n# Todoist Provider\n\n## Resources\n\n- [Todoist OAuth documentation](https://developer.todoist.com/guides/#oauth)\n- [Todoist configuration](https://developer.todoist.com/appconsole.html)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/todoist\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/todoist\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/todoist\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_TODOIST_ID\nAUTH_TODOIST_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Todoist from \"next-auth/providers/todoist\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Todoist],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Todoist from \"@auth/qwik/providers/todoist\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Todoist],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Todoist from \"@auth/sveltekit/providers/todoist\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Todoist],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Todoist from \"@auth/express/providers/todoist\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Todoist] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/trakt.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/trakt.svg\" height=\"64\" width=\"64\" />\n\n# Trakt Provider\n\n## Resources\n\n- [Trakt OAuth documentation](https://trakt.docs.apiary.io/#reference/authentication-oauth)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/trakt\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/trakt\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/trakt\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_TRAKT_ID\nAUTH_TRAKT_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Trakt from \"next-auth/providers/trakt\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Trakt],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Trakt from \"@auth/qwik/providers/trakt\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Trakt],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Trakt from \"@auth/sveltekit/providers/trakt\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Trakt],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Trakt from \"@auth/express/providers/trakt\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Trakt] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- If you're using the api in production by calling `api.trakt.tv`. Follow the example. If you wish to develop on Trakt's sandbox environment by calling `api-staging.trakt.tv`, change the URLs.\n- Trakt does not allow hotlinking images. Even the authenticated user's profile picture.\n- Trakt does not supply the authenticated user's email.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/twitch.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/twitch.svg\" height=\"64\" width=\"64\" />\n\n# Twitch Provider\n\n## Resources\n\n- [Twitch App Console](https://dev.twitch.tv/console/apps)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/twitch\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/twitch\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/twitch\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_TWITCH_ID\nAUTH_TWITCH_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Twitch from \"next-auth/providers/twitch\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Twitch],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Twitch from \"@auth/qwik/providers/twitch\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Twitch],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Twitch from \"@auth/sveltekit/providers/twitch\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Twitch],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Twitch from \"@auth/express/providers/twitch\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Twitch] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Twitch will redirect to the first redirect URI if multiple are added.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/twitter.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/twitter.svg\" height=\"64\" width=\"64\" />\n\n# Twitter/X Provider\n\n## Resources\n\n- [Twitter App documentation](https://developer.twitter.com/en/apps)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/twitter\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/twitter\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/twitter\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_TWITTER_ID\nAUTH_TWITTER_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Twitter from \"next-auth/providers/twitter\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Twitter],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Twitter from \"@auth/qwik/providers/twitter\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Twitter],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Twitter from \"@auth/sveltekit/providers/twitter\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Twitter],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Twitter from \"@auth/express/providers/twitter\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Twitter] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- Auth.js now uses Twitter/X OAuth 2.0 by default. There's no need to set `version` anymore.\n- Email is currently not supported by Twitter/X OAuth 2.0.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/united-effects.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img\n  align=\"right\"\n  src=\"/img/providers/united-effects.svg\"\n  height=\"64\"\n  width=\"64\"\n/>\n\n# United Effects Provider\n\n## Resources\n\n- [UnitedEffects Auth.js documentation](https://docs.unitedeffects.com/integrations/nextauthjs)\",\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/united-effects\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/united-effects\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/united-effects\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_UNITEDEFFECTS_ID\nAUTH_UNITEDEFFECTS_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport UnitedEffects from \"next-auth/providers/united-effects\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [UnitedEffects],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport UnitedEffects from \"@auth/qwik/providers/united-effects\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [UnitedEffects],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport UnitedEffects from \"@auth/sveltekit/providers/united-effects\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [UnitedEffects],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport UnitedEffects from \"@auth/express/providers/united-effects\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [UnitedEffects] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- `issuer` should be the fully qualified URL including your Auth Group ID – e.g. `https://auth.unitedeffects.com/YQpbQV5dbW-224dCovz-3`\n"
  },
  {
    "path": "docs/pages/getting-started/providers/vipps-mobilepay.mdx",
    "content": "---\ntitle: Vipps MobilePay\n---\n\nimport { Code } from \"@/components/Code\"\n\n<img\n  align=\"right\"\n  src=\"/img/providers/vipps-mobilepay.svg\"\n  width=\"116\"\n  height=\"50\"\n/>\n\n# Vipps MobilePay Provider\n\n[Vipps MobilePay](https://vippsmobilepay.com/) is a widespread mobile payment application for mobile in Norway, Sweden, Denmark and Finland. The brand is split, where you have Vipps in Norway and Sweden, and MobilePay in Denmark and Finland, but both brands/apps are using the same API.\n\n## Resources\n\n- [Vipps MobilePay login documentation](https://developer.vippsmobilepay.com/docs/APIs/login-api/)\n- [Official Vipps MobilePay Buttons](https://developer.vippsmobilepay.com/docs/knowledge-base/design-guidelines/buttons/)\n- [Vipps MobilePay Public Testing discovery endpoint](https://apitest.vipps.no/access-management-1.0/access/.well-known/openid-configuration)\n- [Vipps MobilePay Public Production discovery endpoint](https://api.vipps.no/access-management-1.0/access/.well-known/openid-configuration)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/vipps\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/vipps\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/vipps\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_VIPPS_ID\nAUTH_VIPPS_SECRET\n```\n\n### Test API\n\nTo use the test mode, you need to override the issuer with the test API endpoint.\n\n```\nVipps({ issuer: \"https://apitest.vipps.no/access-management-1.0/access/\" })\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Vipps from \"next-auth/providers/vipps\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Vipps],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Vipps from \"@auth/qwik/providers/vipps\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Vipps],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Vipps from \"@auth/sveltekit/providers/vipps\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Vipps],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Vipps from \"@auth/express/providers/vipps\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Vipps] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/vk.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/vk.svg\" width=\"64\" height=\"64\" />\n\n# VK Provider\n\n## Resources\n\n- [VK API documentation](https://vk.com/dev/first_guide)\n- [VK App configuration](https://vk.com/apps?act=manage)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/vk\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/vk\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/vk\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_VK_ID\nAUTH_VK_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Vk from \"next-auth/providers/vk\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Vk],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Vk from \"@auth/qwik/providers/vk\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Vk],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Vk from \"@auth/sveltekit/providers/vk\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Vk],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Vk from \"@auth/express/providers/vk\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Vk] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- By default the provider uses 5.126 version of the API. See https://vk.com/dev/versions for more info. If you want to use a different version, you can pass it to provider's options object:\n\n```ts filename=\"./auth.ts\"\nconst apiVersion = \"5.126\"\n\nexport const { handlers, auth, signin, signout } = NextAuth({\n  providers: [\n    Vk({\n      accessTokenUrl: `https://oauth.vk.com/access_token?v=${apiVersion}`,\n      requestTokenUrl: `https://oauth.vk.com/access_token?v=${apiVersion}`,\n      authorizationUrl: `https://oauth.vk.com/authorize?response_type=code&v=${apiVersion}`,\n      profileUrl: `https://api.vk.com/method/users.get?fields=photo_100&v=${apiVersion}`,\n    }),\n  ],\n})\n```\n"
  },
  {
    "path": "docs/pages/getting-started/providers/webex.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/webex.svg\" height=\"64\" width=\"64\" />\n\n# Webex Provider\n\n## Resources\n\n- [Webex OAuth 2.0 Integration Guide](https://developer.webex.com/docs/integrations)\n- [Login with Webex](https://developer.webex.com/docs/login-with-webex)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/webex\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/webex\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/webex\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_WEBEX_ID\nAUTH_WEBEX_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Webex from \"next-auth/providers/webex\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Webex],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Webex from \"@auth/qwik/providers/webex\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Webex],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Webex from \"@auth/sveltekit/providers/webex\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Webex],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Webex from \"@auth/express/providers/webex\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Webex] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\n- The returned user profile from Webex when using the profile callback. Please refer to [People - Get My Own Details](https://developer.webex.com/docs/api/v1/people/get-my-own-details) on Webex Developer portal for additional fields. Returned fields may vary depending on the user's role, the OAuth integration's scope, and the organization the OAuth integration belongs to.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/wikimedia.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/wikimedia.svg\" height=\"64\" width=\"64\" />\n\n# Wikimedia Provider\n\n## Resources\n\n- [Wikimedia OAuth documentation](https://www.mediawiki.org/wiki/Extension:OAuth)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/wikimedia\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/wikimedia\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/wikimedia\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_WIKIMEDIA_ID\nAUTH_WIKIMEDIA_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Wikimedia from \"next-auth/providers/wikimedia\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Wikimedia],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Wikimedia from \"@auth/qwik/providers/wikimedia\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Wikimedia],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Wikimedia from \"@auth/sveltekit/providers/wikimedia\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Wikimedia],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Wikimedia from \"@auth/express/providers/wikimedia\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Wikimedia] }))\n```\n\n  </Code.Express>\n</Code>\n\n- Go to and accept the Consumer Registration doc: https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration\n- Request a new OAuth 2.0 consumer to get the `clientId` and `clientSecret`: https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration/propose/oauth2\n  - Add the following redirect URL into the console: `http://<your-next-app-url>/api/auth/callback/wikimedia`\n  - Do not check the box next to This consumer is only for **your username**\n  - Unless you explicitly need a larger scope, feel free to select the radio button labelled User identity verification only - no ability to read pages or act on the users behalf.\n\nAfter registration, you can initially test your application only with your own Wikimedia account.\nYou may have to wait several days for the application to be approved for it to be used by everyone.\n\n### Notes\n\nThis provider also supports all Wikimedia projects:\n\n- Wikipedia\n- Wikidata\n- Wikibooks\n- Wiktionary\n- etc..\n\nPlease be aware that Wikimedia accounts do not have to have an associated email address. So you may want to add check if the user has an email address before allowing them to login.\n"
  },
  {
    "path": "docs/pages/getting-started/providers/wordpress.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/wordpress.svg\" height=\"64\" width=\"64\" />\n\n# WordPress Provider\n\n## Resources\n\n- [WordPress OAuth documentation](https://developer.wordpress.com/docs/oauth2/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/wordpress\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/wordpress\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/wordpress\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_WORDPRESS_ID\nAUTH_WORDPRESS_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport WordPress from \"next-auth/providers/wordpress\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [WordPress],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport WordPress from \"@auth/qwik/providers/wordpress\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [WordPress],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport WordPress from \"@auth/sveltekit/providers/wordpress\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [WordPress],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport WordPress from \"@auth/express/providers/wordpress\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [WordPress] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/workos.mdx",
    "content": "---\ntitle: WorkOS\n---\n\nimport { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/workos.svg\" height=\"64\" width=\"64\" />\n\n# WorkOS Provider\n\n## Resources\n\n- [WorkOS SSO OAuth documentation](https://workos.com/docs/reference/sso)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/workos\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/workos\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/workos\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_WORKOS_ID\nAUTH_WORKOS_SECRET\n```\n\nWorkOS also requires you to pass in your `connection` ID to the provider.\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport WorkOS from \"next-auth/providers/workos\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [WorkOS({ connection: \"conn_abc123\" })],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport WorkOS from \"@auth/qwik/providers/workos\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [WorkOS({ connection: \"conn_abc123\" })],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport WorkOS from \"@auth/sveltekit/providers/workos\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [WorkOS({ connection: \"conn_abc123\" })],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport WorkOS from \"@auth/express/providers/workos\"\n\napp.use(\n  \"/auth/*\",\n  ExpressAuth({ providers: [WorkOS({ connection: \"conn_abc123\" })] })\n)\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/yandex.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/yandex.svg\" height=\"64\" width=\"64\" />\n\n# Yandex Provider\n\n## Resources\n\n- [Yandex - Creating an OAuth app](https://yandex.com/dev/id/doc/en/register-client#create)\n- [Yandex - Manage OAuth apps](https://oauth.yandex.com/)\n- [Yandex - OAuth documentation](https://yandex.com/dev/id/doc/en/)\n- [Learn more about OAuth](https://authjs.dev/concepts/oauth)\n- [Source code](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/yandex.ts)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/yandex\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/yandex\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/yandex\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_YANDEX_ID\nAUTH_YANDEX_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Yandex from \"next-auth/providers/yandex\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Yandex],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Yandex from \"@auth/qwik/providers/yandex\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Yandex],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Yandex from \"@auth/sveltekit/providers/yandex\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Yandex],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Yandex from \"@auth/express/providers/yandex\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Yandex] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/zitadel.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/zitadel.svg\" height=\"64\" width=\"64\" />\n\n# Zitadel Provider\n\n## Resources\n\n- [ZITADEL OpenID Endpoints](https://zitadel.com/docs/apis/openidoauth/endpoints)\n- [ZITADEL recommended OAuth Flows](https://zitadel.com/docs/guides/integrate/oauth-recommended-flows)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/zitadel\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/zitadel\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/zitadel\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_ZITADEL_ID\nAUTH_ZITADEL_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Zitadel from \"next-auth/providers/zitadel\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Zitadel],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Zitadel from \"@auth/qwik/providers/zitadel\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Zitadel],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Zitadel from \"@auth/sveltekit/providers/zitadel\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Zitadel],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Zitadel from \"@auth/express/providers/zitadel\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Zitadel] }))\n```\n\n  </Code.Express>\n</Code>\n\n### Notes\n\nThe Redirect URIs used when creating the credentials must include your full domain and end in the callback path. For example:\n\n- For production: `https://{YOUR_DOMAIN}/api/auth/callback/zitadel`\n- For development: `http://localhost:3000/api/auth/callback/zitadel`\n\nMake sure to enable dev mode in ZITADEL console to allow redirects for local development.\n\nZITADEL also returns a email_verified boolean property in the profile. You can use this property to restrict access to people with verified accounts.\n\n```ts filename=pages/api/auth/[...nextauth].js\nconst options = {\n  ...\n  callbacks: {\n    async signIn({ account, profile }) {\n      if (account.provider === \"zitadel\") {\n        return profile.email_verified;\n      }\n      return true; // Do different verification for other providers that don't have `email_verified`\n    },\n  }\n  ...\n}\n```\n"
  },
  {
    "path": "docs/pages/getting-started/providers/zoho.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/zoho.svg\" height=\"64\" width=\"64\" />\n\n# Zoho Provider\n\n## Resources\n\n- [Zoho OAuth 2.0 Integration Guide](https://www.zoho.com/accounts/protocol/oauth/web-server-applications.html)\n- [Zoho API Console](https://api-console.zoho.com)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/zoho\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/zoho\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/zoho\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_ZOHO_ID\nAUTH_ZOHO_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Zoho from \"next-auth/providers/zoho\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Zoho],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Zoho from \"@auth/qwik/providers/zoho\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Zoho],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Zoho from \"@auth/sveltekit/providers/zoho\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Zoho],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Zoho from \"@auth/express/providers/zoho\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Zoho] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/providers/zoom.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n<img align=\"right\" src=\"/img/providers/zoom.svg\" height=\"64\" width=\"64\" />\n\n# Zoom Provider\n\n## Resources\n\n- [Zoom OAuth 2.0 Integration Guide](https://developers.zoom.us/docs/integrations/oauth/)\n\n## Setup\n\n### Callback URL\n\n<Code>\n  <Code.Next>\n\n```bash\nhttps://example.com/api/auth/callback/zoom\n```\n\n  </Code.Next>\n  <Code.Qwik>\n\n```bash\nhttps://example.com/auth/callback/zoom\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```bash\nhttps://example.com/auth/callback/zoom\n```\n\n  </Code.Svelte>\n</Code>\n\n### Environment Variables\n\n```\nAUTH_ZOOM_ID\nAUTH_ZOOM_SECRET\n```\n\n### Configuration\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"/auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Zoom from \"next-auth/providers/zoom\"\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [Zoom],\n})\n```\n\n  </Code.Next>\n  <Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Zoom from \"@auth/qwik/providers/zoom\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Zoom],\n  })\n)\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\n```ts filename=\"/src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport Zoom from \"@auth/sveltekit/providers/zoom\"\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers: [Zoom],\n})\n```\n\n  </Code.Svelte>\n  <Code.Express>\n\n```ts filename=\"/src/app.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport Zoom from \"@auth/express/providers/zoom\"\n\napp.use(\"/auth/*\", ExpressAuth({ providers: [Zoom] }))\n```\n\n  </Code.Express>\n</Code>\n"
  },
  {
    "path": "docs/pages/getting-started/session-management/_meta.js",
    "content": "export default {\n  login: \"Signin and Signout\",\n  \"get-session\": \"Get Session\",\n  protecting: \"Protecting Resources\",\n  \"custom-pages\": \"Custom Pages\",\n}\n"
  },
  {
    "path": "docs/pages/getting-started/session-management/custom-pages.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n# Custom Pages\n\nTo enable custom pages add the following to your Auth.js configuration. In the `pages` object, the key is the type of page and the value is the path/route at which the page is located. Please make sure you actually have a page at the specified route.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\" {8-10}\nimport { NextAuth } from \"next-auth\"\nimport GitHub from \"next-auth/providers/github\"\n\n// Define your configuration in a separate variable and pass it to NextAuth()\n// This way we can also 'export const config' for use later\nexport const config = {\n  providers: [GitHub],\n  pages: {\n    signIn: \"/login\",\n  },\n}\n\nexport const { signIn, signOut, handle } = NextAuth(config)\n```\n\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport GitHub from \"@auth/qwik/providers/github\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [GitHub],\n    pages: {\n      signIn: \"/login\",\n    },\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"src/auth.ts\" {14-16}\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport GitHub from \"@auth/sveltekit/providers/github\"\nimport type { Provider } from \"@auth/sveltekit/providers\"\n\nconst providers: Provider[] = [GitHub]\n\n// Export this map of provider details to use in the sign-in page later\nexport const providerMap = providers.map((provider) => {\n  return { id: provider.id, name: provider.name }\n})\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers,\n  pages: {\n    signIn: \"/signin\",\n  },\n})\n```\n\n```ts filename=\"src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"src/routes/auth.route.ts\" {12-14}\nimport express from \"express\"\nimport { ExpressAuth } from \"@auth/express\"\nimport GitHub from \"@auth/express/providers/github\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    providers: [GitHub],\n    pages: {\n      signIn: \"/signin\",\n    },\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\nTo continue setting up the custom page, checkout our [guide on custom pages](/guides/pages/signin).\n"
  },
  {
    "path": "docs/pages/getting-started/session-management/get-session.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n# Get Session\n\nOnce a user is logged in, you often want to get the session object in order to use the data in some way. A common use-case is to show their profile picture or display some other user information.\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"./components/UserAvatar.tsx\" {4} /async/\nimport { auth } from \"../auth\"\n\nexport default async function UserAvatar() {\n  const session = await auth()\n\n  if (!session?.user) return null\n\n  return (\n    <div>\n      <img src={session.user.image} alt=\"User Avatar\" />\n    </div>\n  )\n}\n```\n\n</Code.Next>\n<Code.NextClient>\n\nAlthough `next-auth` supports client-side data retrieval using `useSession` and `SessionProvider` for both the App Router and Pages Router, in real-world scenarios, these are used less frequently. Typically, you'll want to take full advantage of server-side rendering to optimize performance and security.\n\n### App Router\n\n```tsx filename=\"app/admin/dashboard.tsx\"\n\"use client\"\nimport { useSession } from \"next-auth/react\"\n\nexport default function Dashboard() {\n  const { data: session } = useSession()\n\n  if (session?.user?.role === \"admin\") {\n    return <p>You are an admin, welcome!</p>\n  }\n\n  return <p>You are not authorized to view this page!</p>\n}\n```\n\n```tsx filename=\"app/admin/page.tsx\"\nimport { SessionProvider } from \"next-auth/react\"\nimport { Dashboard } from \"./Dashboard\"\n\nexport default function Administrator() {\n  return (\n    <SessionProvider>\n      <Dashboard />\n    </SessionProvider>\n  )\n}\n```\n\n### Page Server Side\n\nIn the pages router, to access a session in a component, you'll first need to get the `session` object in a page and then pass it down to the component.\n\n```tsx filename=\"./pages/dashboard.tsx\"\nimport { auth } from \"@/auth.ts\"\nimport { UserAvatar } from \"@/components/UserAvatar\"\n\nexport default function Dashboard({ session }) {\n  return (\n    <nav>\n      <UserAvatar session={session} />\n    </nav>\n  )\n}\n\nexport async function getServerSideProps(ctx) {\n  const session = await auth(ctx)\n\n  return {\n    props: {\n      session,\n    },\n  }\n}\n```\n\n### Page Client Side\n\nWhen accessing the session client-side using `useSession()`, make sure an Auth.js `<SessionProvider />` is\nwrapping your page.\n\n```tsx filename=\"pages/_app.tsx\"\nimport type { AppProps } from \"next/app\"\nimport { SessionProvider } from \"next-auth/react\"\n\nexport default function MyApp({\n  Component,\n  pageProps: { session, ...pageProps },\n}: AppProps) {\n  return (\n    <SessionProvider session={session}>\n      <Component {...pageProps} />;\n    </SessionProvider>\n  )\n}\n```\n\n```tsx filename=\"pages/dashboard.tsx\"\nimport { useSession } from \"next-auth/react\"\nimport { UserAvatar } from \"@/components/UserAvatar\"\n\nexport default function Dashboard() {\n  const { data: session } = useSession()\n\n  return (\n    <nav>\n      <UserAvatar session={session} />\n    </nav>\n  )\n}\n```\n\nFinally, we can use it in the component.\n\n```tsx filename=\"./components/UserAvatar.tsx\" /session/\nimport type { Session } from \"next-auth\"\n\nexport function UserAvatar({ session }: { session: Session | null }) {\n  return (\n    <div>\n      <img\n        src={session?.user?.image ?? \"https://i.pravatar.cc/300\"}\n        alt=\"User Avatar\"\n      />\n    </div>\n  )\n}\n```\n\n</Code.NextClient>\n<Code.Qwik>\n\nUnder the hood Qwik is preparing automatically the session for you so you don't have to implement custom logic for that.\nYou can read the sesion on the server with `event.sharedMap.get(\"session\")` and on the client with the `useSession()` action.\n\n</Code.Qwik>\n<Code.Svelte>\n\nWith SvelteKit, you have to return the `session` object from the load function in your `+page.server.ts` or `+layout.server.ts` files.\n\n```ts filename=\"src/routes/+page.server.ts\" {11}\nimport type { PageServerLoad } from \"./$types\"\n\nexport const load: PageServerLoad = async ({ params, locals }) => {\n  const session = await locals.auth()\n\n  if (!session?.user?.userId) {\n    redirect(303, `/login`)\n  }\n\n  return {\n    session,\n  }\n}\n```\n\nThen you can access the `session` on the `$page.data` object in your page.\n\n```svelte filename=\"src/routes/+page.svelte\" {7}\n<script lang=\"ts\">\n  const { data } = $props()\n</script>\n\n<nav>\n  <img\n    src={data.session?.user?.image ?? \"https://i.pravatar.cc/300\"}\n    alt=\"User Avatar\"\n  />\n</nav>\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"app.ts\"\nimport { getSession } from \"@auth/express\"\n\nexport function authSession(req: Request, res: Response, next: NextFunction) {\n  res.locals.session = await getSession(req)\n  next()\n}\n\napp.use(authSession)\n\n// Now in your route\napp.get(\"/\", (req, res) => {\n  const { session } = res.locals\n  res.render(\"index\", { user: session?.user })\n})\n```\n\n</Code.Express>\n</Code>\n\nIf you'd like to extend your session with more fields from your OAuth provider, for example, please check out our [\"extending the session\" guide](/guides/extending-the-session).\n\n<Callout>\n  By default, GET requests to the session endpoint will automatically return the\n  headers to prevent caching.\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/session-management/login.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Screenshot } from \"@/components/Screenshot\"\nimport { Code } from \"@/components/Code\"\n\n# Handling Signin and Signout\n\nTo signin your users, make sure you have at least one [authentication method](/getting-started/authentication) setup. You then need to build a button which will call the sign in function from your Auth.js framework package.\n\n<Code>\n  <Code.Next>\n\n```tsx filename=\"./components/auth/signin-button.tsx\"\nimport { signIn } from \"@/auth\"\n\nexport function SignIn() {\n  return (\n    <form\n      action={async () => {\n        \"use server\"\n        await signIn()\n      }}\n    >\n      <button type=\"submit\">Sign in</button>\n    </form>\n  )\n}\n```\n\n  </Code.Next>\n  <Code.NextClient>\n\n```tsx filename=\"./components/auth/signin-button.tsx\"\n\"use client\"\nimport { signIn } from \"next-auth/react\"\n\nexport function SignIn() {\n  return <button onClick={() => signIn()}>Sign In</button>\n}\n```\n\n  </Code.NextClient>\n  <Code.Qwik>\n\nWith Qwik we can do a server-side sign in with Form action, or a more simple client-side login via submit method.\n\n```ts filename=\"./components/sign-in.tsx\"\nimport { component$ } from \"@builder.io/qwik\"\nimport { Form } from \"@builder.io/qwik-city\"\nimport { useSignIn } from \"./plugin@auth\"\n\nexport default component$(() => {\n  const signInSig = useSignIn()\n\n  return (\n    <>\n      {/* server-side login with Form action */}\n      <Form action={signInSig}>\n        <input type=\"hidden\" name=\"providerId\" value=\"${providerId}\" />\n        <input\n          type=\"hidden\"\n          name=\"options.redirectTo\"\n          value=\"/\"\n        />\n        <button>Sign In</button>\n      </Form>\n\n      {/* submit method */}\n      <Link\n        onClick$={() => signInSig.submit({ redirectTo: \"/\" })}\n      >\n        SignIn\n      </Link>\n    </>\n  )\n})\n```\n\n  </Code.Qwik>\n  <Code.Svelte>\n\nThe SvelteKit client supports two signin and signout methods, one server-side using Form Actions, and one client-side using requests and redirects.\n\n#### Form Action (Server-Side)\n\nTo signin your users using a SvelteKit form action, we can use the `SignIn` component exported from `@auth/sveltekit/components`.\n\n```svelte filename=\"src/routes/+page.svelte\" {7-9}\n<script lang=\"ts\">\n  import { SignIn } from \"@auth/sveltekit/components\"\n</script>\n\n<header>\n  <nav>\n    <SignIn>\n      <span slot=\"submitButton\">Sign In with GitHub</span>\n    </SignIn>\n  </nav>\n</header>\n```\n\nThis requires a server action at `/signin`, this path can be customized with the `signInPage` prop on the `SignIn` component.\n\n```ts filename=\"src/routes/signin/+page.server.ts\"\nimport { signIn } from \"../../auth\"\nimport type { Actions } from \"./$types\"\n\nexport const actions: Actions = { default: signIn }\n```\n\n#### Client Side\n\nClient-side is a bit simpler as we just need to import a button `on:click` handler from `@auth/sveltekit/client`.\n\n```svelte filename=\"src/routes/+page.svelte\" {2, 8}\n<script lang=\"ts\">\n  import { signIn } from \"@auth/sveltekit/client\"\n</script>\n\n<div>\n  <nav>\n    <img src=\"/img/logo.svg\" alt=\"Company Logo\" />\n    <button on:click={signIn}>Signin</button>\n  </nav>\n</div>\n```\n\nJust like in other frameworks, you can also pass a provider to the `signIn` function which will attempt to login directly with that provider.\n\n  </Code.Svelte>\n  <Code.Express>\n\nThe Express package runs server-side and therefore it doesn't make sense to create a \"SignIn button component\". However, to signin or signout with Express, send a request to the appropriate [REST API Endpoints](/reference/core/types#authaction) from your client (i.e. `/auth/signin`, `/auth/signout`, etc.).\n\nTo sign in users with Express, you can create a route that handles the sign-in logic. Here is an example:\n\n```ts filename=\"src/routes/auth.ts\"\nimport express, { Request, Response } from \"express\"\nimport { signIn } from \"../auth\"\nconst router = express.Router()\n\nrouter.post(\"/auth/signin\", async (req: Request, res: Response) => {\n  try {\n    await signIn(req, res)\n    res.redirect(\"/dashboard\")\n  } catch (error) {\n    res.status(500).send(\"Sign in failed\")\n  }\n})\n\nexport { router }\n```\n\nTo sign out users with Express, you can create a route that handles the sign-out logic. Here is an example:\n\n```ts filename=\"src/routes/auth.ts\"\nimport express, { Request, Response } from \"express\"\nimport { signOut } from \"../auth\"\nconst router = express.Router()\n\nrouter.post(\"/auth/signout\", async (req: Request, res: Response) => {\n  try {\n    await signOut(req, res)\n    res.redirect(\"/\")\n  } catch (error) {\n    res.status(500).send(\"Sign out failed\")\n  }\n})\n\nexport { router }\n```\n\n  </Code.Express>\n</Code>\n\nYou can also pass a provider to the `signIn` function which will attempt to login directly with that provider. Otherwise, when clicking this button in your application, the user will be redirected to the configured sign in page. If you did not setup a [custom sign in page](/guides/pages/signin), the user will be redirected to the default signin page at `/[basePath]/signin`.\n\nimport DefaultSignInPage from \"../../../public/img/getting-started/default-signin-page.webp\"\n\n<Screenshot src={DefaultSignInPage} alt=\"Default Sign-in Page\" />\n\nOnce authenticated, the user will be redirected back to the page they started the signin from. If\nyou want the user to be redirected somewhere else after sign in (.i.e `/dashboard`), you can do so\nby passing the target URL as `redirectTo` in the sign-in options.\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"app/components/signin-button.tsx\" {8}\nimport { signIn } from \"@/auth.ts\"\n\nexport function SignIn() {\n  return (\n    <form\n      action={async () => {\n        \"use server\"\n        await signIn(\"github\", { redirectTo: \"/dashboard\" })\n      }}\n    >\n      <button type=\"submit\">Sign in</button>\n    </form>\n  )\n}\n```\n\n</Code.Next>\n<Code.NextClient>\n\n```tsx filename=\"src/components/signin-button.tsx\" {5}\n\"use client\"\nimport { signIn } from \"next-auth/react\"\n\nexport function SignIn() {\n  return (\n    <button onClick={() => signIn(\"github\", { redirectTo: \"/dashboard\" })}>\n      Sign In\n    </button>\n  )\n}\n```\n\n</Code.NextClient>\n<Code.Qwik>\n\n```ts filename=\"./components/sign-in.tsx\"\nimport { component$ } from \"@builder.io/qwik\"\nimport { useSignIn } from \"./plugin@auth\"\n\nexport default component$(() => {\n  const signInSig = useSignIn()\n\n  return (\n    <>\n      <Link\n        onClick$={() => signInSig.submit({ redirectTo: \"/dashboard\" })}\n      >\n        SignIn\n      </Link>\n    </>\n  )\n})\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```svelte filename=\"src/routes/+page.svelte\" {9-13}\n<script lang=\"ts\">\n  import { page } from \"$app/stores\"\n  import { SignIn } from \"@auth/sveltekit/components\"\n</script>\n\n<header>\n  <nav>\n    <SignIn\n      options={{\n        redirectTo: $page.data.redirectTo\n          ? `/${decodeURIComponent($page.data.redirectTo).slice(1)}`\n          : `/dashboard`,\n      }}\n      className=\"w-full\"\n    >\n      <span slot=\"submitButton\">Sign in</span>\n    </SignIn>\n  </nav>\n</header>\n```\n\n</Code.Svelte>\n<Code.Express>\n```ts filename=\"src/routes/auth.ts\"\nimport express, { Request, Response } from \"express\";\nimport { signOut } from \"../auth\"; \nconst router = express.Router()\n\nrouter.post(\"/auth/signout\", async (req: Request, res: Response) => {\ntry {\nawait signOut(req, res)\nres.redirect(\"/\")\n} catch (error) {\nres.status(500).send(\"Sign out failed\")\n}\n})\n\nexport { router }\n```\n</Code.Express>\n</Code>\n\n### Signout\n\nSigning out can be done similarly to signing in. Most frameworks offer both a client-side and server-side method for signing out as well.\n\n<Code>\n<Code.Next>\n\nTo sign out users with a form action, you can build a button that calls the exported signout function from your Auth.js config.\n\n```tsx filename=\"app/components/signout-button.tsx\" {8}\nimport { signOut } from \"@/auth.ts\"\n\nexport function SignOut() {\n  return (\n    <form\n      action={async () => {\n        \"use server\"\n        await signOut()\n      }}\n    >\n      <button type=\"submit\">Sign Out</button>\n    </form>\n  )\n}\n```\n\n</Code.Next>\n<Code.NextClient>\n\n```tsx filename=\"src/components/signout-button.tsx\" {5}\n\"use client\"\nimport { signOut } from \"next-auth/react\"\n\nexport function SignOut() {\n  return <button onClick={() => signOut()}>Sign Out</button>\n}\n```\n\n</Code.NextClient>\n<Code.Qwik>\n\nWith Qwik we can do a server-side sign out with Form action, or a more simple client-side sign out via submit method.\n\n```ts filename=\"./components/sign-out.tsx\"\nimport { component$ } from \"@builder.io/qwik\"\nimport { Form, Link } from \"@builder.io/qwik-city\"\nimport { useSignOut } from \"./plugin@auth\"\n\nexport default component$(() => {\n  const signOutSig = useSignOut()\n\n  return (\n    <>\n      {/* server-side with Form action */}\n      <Form action={signOutSig}>\n        <input type=\"hidden\" name=\"redirectTo\" value=\"/signedout\" />\n        <button>Sign Out</button>\n      </Form>\n\n      {/* submit method */}\n      <Link onClick$={() => signOutSig.submit({ redirectTo: \"/\" })}>\n        SignIn\n      </Link>\n    </>\n  )\n})\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\nSvelteKit supports both server and client-side methods for signing out as well.\n\n#### Server-side\n\nTo use the SvelteKit form action for signing out, we can use the `SignOut` component exported from `@auth/sveltekit/components`.\n\n```svelte filename=\"src/routes/+page.svelte\" {2, 7-9}\n<script lang=\"ts\">\n  import { SignOut } from \"@auth/sveltekit/components\"\n</script>\n\n<header>\n  <nav>\n    <SignOut>\n      <span slot=\"submitButton\">Signout</span>\n    </SignOut>\n  </nav>\n</header>\n```\n\nThis requires a server action at `/signout`, this path can be customized with the `signOutPage` prop on the `<SignOut>` component.\n\n```ts filename=\"src/routes/signout/+page.server.ts\"\nimport { signOut } from \"../../auth\"\nimport type { Actions } from \"./$types\"\n\nexport const actions: Actions = { default: signOut }\n```\n\n#### Client Side\n\nClient-side is a bit simpler as we just need to import a button `on:click` handler from `@auth/sveltekit/client`.\n\n```svelte filename=\"src/routes/+page.svelte\" {8}\n<script lang=\"ts\">\n  import { signOut } from \"@auth/sveltekit/client\"\n</script>\n\n<div>\n  <nav>\n    <img src=\"/img/logo.svg\" alt=\"Company Logo\" />\n    <button on:click={signOut}>Signout</button>\n  </nav>\n</div>\n```\n\n</Code.Svelte>\n<Code.Express>\n\nThe Express package runs server-side and therefore it doesn't make sense to create a \"SignIn button component\". However, to signin or signout with Express, send a request to the appropriate [REST API Endpoints](/reference/core/types#authaction) from your client (i.e. `/auth/signin`, `/auth/signout`, etc.).\n\nTo sign in users with Express, you can create a route that handles the sign-in logic. Here is an example:\n\n```ts filename=\"src/routes/auth.ts\"\nimport express, { Request, Response } from \"express\"\nimport { signIn } from \"../auth\"\nconst router = express.Router()\n\nrouter.post(\"/auth/signin\", async (req: Request, res: Response) => {\n  try {\n    await signIn(req, res)\n    res.redirect(\"/dashboard\")\n  } catch (error) {\n    res.status(500).send(\"Sign in failed\")\n  }\n})\n\nexport { router }\n```\n\nTo sign out users with Express, you can create a route that handles the sign-out logic. Here is an example:\n\n```ts filename=\"src/routes/auth.ts\"\nimport express, { Request, Response } from \"express\"\nimport { signOut } from \"../auth\"\nconst router = express.Router()\n\nrouter.post(\"/auth/signout\", async (req: Request, res: Response) => {\n  try {\n    await signOut(req, res)\n    res.redirect(\"/\")\n  } catch (error) {\n    res.status(500).send(\"Sign out failed\")\n  }\n})\n\nexport { router }\n```\n\n</Code.Express>\n</Code>\n\n<Callout>\n  Note that when signing out of an OAuth provider like GitHub in an Auth.js\n  application, the user will not be signed out of GitHub elsewhere.\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/session-management/protecting.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n# Protecting Resources\n\nProtecting routes can be done generally by checking for the session and taking an action if an active session is not found, like redirecting the user to the login page or simply returning a `401: Unauthenticated` response.\n\n### Pages\n\n<Code>\n<Code.Next>\n\nYou can use the `auth` function returned from `NextAuth()` and exported from your `auth.ts` or `auth.js` configuration file to get the session object.\n\n```tsx filename=\"app/server/page.tsx\" {4}\nimport { auth } from \"@/auth\"\n\nexport default async function Page() {\n  const session = await auth()\n  if (!session) return <div>Not authenticated</div>\n\n  return (\n    <div>\n      <pre>{JSON.stringify(session, null, 2)}</pre>\n    </div>\n  )\n}\n```\n\n</Code.Next>\n<Code.NextClient>\n\nTo protect a page in the Next.js Pages router, we can use `auth` in `getServerSideProps` to return the `session` to the page as props.\n\n```tsx filename=\"./pages/dashboard.tsx\" {4, 10}\nimport { auth } from \"../auth\"\n\nexport default function Dashboard({ session }) {\n  if (!session.user) return <div>Not authenticated</div>\n\n  return <div>{JSON.stringify(session, null, 2)}</div>\n}\n\nexport async function getServerSideProps(ctx) {\n  const session = await auth(ctx)\n\n  return {\n    props: {\n      session,\n    },\n  }\n}\n```\n\nTo access the session client-side using `useSession()`. Make sure `<SessionProvider />` is\nwrapping your application.\n\n```tsx filename=\"./pages/_app.tsx\"\nimport type { AppProps } from \"next/app\"\nimport { SessionProvider } from \"next-auth/react\"\n\nexport default function MyApp({\n  Component,\n  pageProps: { session, ...pageProps },\n}: AppProps) {\n  return (\n    <SessionProvider session={session}>\n      <Component {...pageProps} />;\n    </SessionProvider>\n  )\n}\n```\n\n</Code.NextClient>\n<Code.Qwik>\n\nInside component$ you can use `useSession` loader to retrieve the current sessionStorage.\n\n```ts\nimport { component$ } from '@builder.io/qwik';\nimport { useSession } from '~/routes/plugin@auth';\n\nexport default component$(() => {\n  const session = useSession();\n  return <p>{session.value?.user?.email}</p>;\n});\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\nIn SvelteKit, you can leverage the `event.locals.auth()` function that is put there by the Auth.js `handle` function we're importing and using in `hooks.server.ts`.\n\nBy calling `event.locals.auth()` server-side, we can check for the session in any `+page.server.ts` or `+layout.server.ts` file and either allow the request on, or redirect to the `/login` page, for example.\n\n```ts filename=\"src/routes/dashboard/+page.server.ts\" {5}\nimport { fail, redirect } from \"@sveltejs/kit\"\nimport type { PageServerLoad } from \"./$types\"\n\nexport const load: PageServerLoad = async (event) => {\n  const session = await event.locals.auth()\n\n  if (!session?.user?.userId) {\n    return fail(401, { type: \"error\", error: \"Unauthenticated\" })\n  }\n\n  return {\n    session,\n  }\n}\n```\n\n</Code.Svelte>\n<Code.Express>\n\nYou can protect routes by checking for the presence of a session and then redirect to a login page if the session is not present. This can either be done per route, or for a group of routes using a middleware such as the following:\n\n```ts filename=\"lib.ts\"\nimport { getSession } from \"@auth/express\"\n\nexport async function authenticatedUser(\n  req: Request,\n  res: Response,\n  next: NextFunction\n) {\n  const session = res.locals.session ?? (await getSession(req, authConfig))\n  if (!session?.user) {\n    res.redirect(\"/login\")\n  } else {\n    next()\n  }\n}\n```\n\n```ts filename=\"app.ts\"\nimport { authenticatedUser } from \"./lib.ts\"\n\n// This route is protected\napp.get(\"/profile\", authenticatedUser, (req, res) => {\n  const { session } = res.locals\n  res.render(\"profile\", { user: session?.user })\n})\n\n// This route is not protected\napp.get(\"/\", (req, res) => {\n  res.render(\"index\")\n})\n\napp.use(\"/\", root)\n```\n\n</Code.Express>\n</Code>\n\n### API Routes\n\nProtecting API routes in the various frameworks can also be done with the `auth` export.\n\n<Code>\n<Code.Next>\n\nIn Next.js, you can use the `auth` function to wrap an API route handler. The request parameter will then have an `auth` key on it which you can check for a valid session.\n\n```ts filename=\"./app/api/admin/route.ts\" {4}\nimport { auth } from \"@/auth\"\nimport { NextResponse } from \"next/server\"\n\nexport const GET = auth(function GET(req) {\n  if (req.auth) return NextResponse.json(req.auth)\n  return NextResponse.json({ message: \"Not authenticated\" }, { status: 401 })\n})\n```\n\n</Code.Next>\n<Code.NextClient>\n\n```ts filename=\"./pages/api/admin.ts\"\n// TODO: Update once server-side API methods are implemented for pages router again\n\n// import { auth } from \"../../auth\"\n// import { getSession } from \"next-auth/react\"\nimport { NextApiRequest, NextApiResponse } from \"next\"\n\nexport default async function handler(\n  req: NextApiRequest,\n  res: NextApiResponse\n) {\n  // const session = await auth(req, res)\n  // const session = await getSession(req, res)\n  const url = `${req.headers[\"x-forwarded-proto\"]}://${req.headers.host}/api/auth/session`\n\n  const sessionRes = await fetch(url)\n  const session = await sessionRes.json()\n\n  if (!session.user) {\n    return res.status(401).json({ message: \"Not authenticated\" })\n  }\n\n  return res.json({ data: \"Protected data\" })\n}\n```\n\n</Code.NextClient>\n<Code.Qwik>\n\nSession data can be accessed via the route event.sharedMap.\nSo a route can be protected and redirect using something like this located in a layout.tsx or page index.tsx:\n\n```ts\nexport const onRequest: RequestHandler = (event) => {\n  const session = event.sharedMap.get(\"session\")\n  if (!session || new Date(session.expires) < new Date()) {\n    throw event.redirect(302, `/`)\n  }\n}\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\nAPI Routes in SvelteKit work like any other server-side file in Auth.js in SvelteKit, you can access the session by calling `event.locals.auth()` in the `+server.ts` files as well.\n\n```ts filename=\"src/routes/api/users/+server.ts\"\nimport type { RequestHandler } from \"./$types\"\n\nexport const GET: RequestHandler = async (event) => {\n  const session = await event.locals.auth()\n\n  if (!session?.user?.userId) {\n    return new Response(null, { status: 401, statusText: \"Unauthorized\" })\n  }\n}\n```\n\n</Code.Svelte>\n<Code.Express>\n\nAPI Routes are protected in the same way as any other route in Express, see [the examples above](/getting-started/session-management/protecting?framework=express#pages).\n\n</Code.Express>\n</Code>\n\n### Next.js Proxy\n\nWith Next.js 16+, the easiest way to protect a set of pages is using the proxy file. You can create a `proxy.ts` file in your root pages directory with the following contents.\n\n<Callout type=\"info\">\n  As of Next.js 16, `middleware.ts` has been renamed to `proxy.ts` and the\n  exported function has been renamed from `middleware` to `proxy`. If you are\n  using an older version of Next.js, use `middleware.ts` and export `auth` as\n  `middleware` instead.\n</Callout>\n\n```ts filename=\"proxy.ts\"\nexport { auth as proxy } from \"@/auth\"\n```\n\nThen define `authorized` callback in your `auth.ts` file. For more details check out the [reference docs](/reference/nextjs#authorized).\n\n```ts filename=\"auth.ts\"\nimport NextAuth from \"next-auth\"\n\nexport const { auth, handlers } = NextAuth({\n  callbacks: {\n    authorized: async ({ auth }) => {\n      // Logged in users are authenticated, otherwise redirect to login page\n      return !!auth\n    },\n  },\n})\n```\n\nYou can also use the `auth` method as a wrapper if you'd like to implement more logic inside the proxy.\n\n```ts filename=\"proxy.ts\"\nimport { auth } from \"@/auth\"\n\nexport const proxy = auth((req) => {\n  if (!req.auth && req.nextUrl.pathname !== \"/login\") {\n    const newUrl = new URL(\"/login\", req.nextUrl.origin)\n    return Response.redirect(newUrl)\n  }\n})\n```\n\nYou can also use a regex to match multiple routes or you can negate certain routes in order to protect all remaining routes. The following example avoids running the proxy on paths such as the favicon or static images.\n\n```ts filename=\"proxy.ts\"\nexport const config = {\n  matcher: [\"/((?!api|_next/static|_next/image|favicon.ico).*)\"],\n}\n```\n\nThe proxy will protect pages as defined by the `matcher` config export. For more details about the matcher, check out the [Next.js docs](https://nextjs.org/docs/app/api-reference/file-conventions/proxy).\n\n<Callout>\n  You should not rely on the proxy exclusively for authorization. Always ensure\n  that the session is verified as close to your data fetching as possible.\n</Callout>\n"
  },
  {
    "path": "docs/pages/getting-started/typescript.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Screenshot } from \"@/components/Screenshot\"\nimport { Code } from \"@/components/Code\"\n\n# TypeScript\n\nAuth.js is committed to type-safety, so it's written in TypeScript and 100% type safe. It comes with its own type definitions to use in your project.\n\nEven if you don't use TypeScript, IDEs like VS Code will pick this up to provide you with a better developer experience. While you are typing, you will get suggestions about what certain objects/functions look like, and sometimes links to documentation, examples, and other valuable resources.\n\n## Philosophy\n\nWe have chosen [module\naugmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation)\nover [generics](https://www.typescriptlang.org/docs/handbook/2/generics.html) as the main technique to type Auth.js resources across your application in case you extend them.\n\n<details>\n<summary>\n<b>Why not use <a href=\"https://www.typescriptlang.org/docs/handbook/2/generics.html\">generics</a>?</b>\n</summary>\nThe interfaces that are shared across submodules are not passed to Auth.js library functions as generics.\n\nWhenever these types are used, the functions always expect to return these formats. With generics, one might be able to override the type in one place, but not the other, which would cause the types to be out of sync with the implementation.\n\nWith module augmentation, you defined the types once, and you can be sure that they are always the same where it's expected.\n\n</details>\n\n## Module Augmentation\n\nAuth.js libraries come with certain interfaces that are shared across submodules and different Auth.js libraries (For example: `next-auth` and `@auth/prisma-adapter` will rely on types from `@auth/core`).\n\nGood examples of such interfaces are `Session` or `User`. You can use TypeScript's [Module\nAugmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) to extend these types to add your own properties across Auth.js without having to pass generic all over the place.\n\nLet's look at extending `Session` for example.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"auth.ts\"\nimport NextAuth, { type DefaultSession } from \"next-auth\"\n\ndeclare module \"next-auth\" {\n  /**\n   * Returned by `auth`, `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context\n   */\n  interface Session {\n    user: {\n      /** The user's postal address. */\n      address: string\n      /**\n       * By default, TypeScript merges new interface properties and overwrites existing ones.\n       * In this case, the default session user properties will be overwritten,\n       * with the new ones defined above. To keep the default session user properties,\n       * you need to add them back into the newly declared interface.\n       */\n    } & DefaultSession[\"user\"]\n  }\n}\n\nexport const { auth, handlers } = NextAuth({\n  callbacks: {\n    session({ session, token, user }) {\n      // `session.user.address` is now a valid property, and will be type-checked\n      // in places like `useSession().data.user` or `auth().user`\n      return {\n        ...session,\n        user: {\n          ...session.user,\n          address: user.address,\n        },\n      }\n    },\n  },\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"plugin@auth.ts\"\nimport { DefaultSession, QwikAuth$ } from \"@auth/qwik\"\n\ndeclare module \"@auth/qwik\" {\n  /**\n   * Returned by the `useSession` hook and the `session` object in the sharedMap\n   */\n  interface Session {\n    user: {\n      /** The user's postal address. */\n      address: string\n      /**\n       * By default, TypeScript merges new interface properties and overwrites existing ones.\n       * In this case, the default session user properties will be overwritten,\n       * with the new ones defined above. To keep the default session user properties,\n       * you need to add them back into the newly declared interface.\n       */\n    } & DefaultSession[\"user\"]\n  }\n}\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    callbacks: {\n      session({ session, token, user }) {\n        // `session.user.address` is now a valid property, and will be type-checked\n        // in places like `useSession().user` or `sharedMap.get('session').user`\n        return {\n          ...session,\n          user: {\n            ...session.user,\n            address: user.address,\n          },\n        }\n      },\n    },\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"auth.ts\"\nimport SvelteKitAuth, { type DefaultSession } from \"@auth/sveltekit\"\n\ndeclare module \"@auth/sveltekit\" {\n  interface Session {\n    user: {\n      userId: string\n      /**\n       * By default, TypeScript merges new interface properties and overwrites existing ones.\n       * In this case, the default session user properties will be overwritten,\n       * with the new ones defined above. To keep the default session user properties,\n       * you need to add them back into the newly declared interface.\n       */\n    } & DefaultSession[\"user\"]\n  }\n}\n\nexport const { handle } = SvelteKitAuth({\n  callbacks: {\n    session: async ({ session, token }) => {\n      if (token) {\n        session.user.userId = token.sub\n      }\n      // `session.user.userId` is now a valid property, and will be type-checked\n      // in places like `useSession().data.user` or `auth().user`\n      return session\n    },\n  },\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n```ts filename=\"auth.ts\"\nimport { ExpressAuthConfig } from \"@auth/express\";\n// Extend the default Session type to include custom properties\ndeclare module \"@auth/express\" {\n  interface Session {\n    user: {\n      id: string; // Add a custom `id` property to the session user object\n    };\n  }\n}\n\nexport const authConfig: ExpressAuthConfig = {\n  callbacks: {\n    /**\n     * The `session` callback is used to customize the session object\n     * returned to the client. Here, we add a custom `id` property to\n     * the session user object, which is populated from the JWT token.\n     *\n     * @param session - The current session object.\n     * @param token - The JWT token containing user information.\n     * @returns The modified session object with the custom `id` property.\n     */\n    async session({ session, token }) {\n      if (token.sub) {\n        // Add the `id` property to the session user object\n        session.user.id = token.sub; // `token.sub` contains the user ID\n      }\n      return session;\n    },\n  },\n};\n```\n</Code.Express>\n</Code>\n\nModule augmentation is not limited to specific interfaces. You can augment any `interface` we've defined, here are some of the more common interfaces that you might want to override based on your use case.\n\n```ts filename=\"types.d.ts\"\ndeclare module \"next-auth\" {\n  /**\n   * The shape of the user object returned in the OAuth providers' `profile` callback,\n   * or the second parameter of the `session` callback, when using a database.\n   */\n  interface User {}\n  /**\n   * The shape of the account object returned in the OAuth providers' `account` callback,\n   * Usually contains information about the provider being used, like OAuth tokens (`access_token`, etc).\n   */\n  interface Account {}\n\n  /**\n   * Returned by `useSession`, `auth`, contains information about the active session.\n   */\n  interface Session {}\n}\n\n// The `JWT` interface can be found in the `next-auth/jwt` submodule\nimport { JWT } from \"next-auth/jwt\"\n\ndeclare module \"next-auth/jwt\" {\n  /** Returned by the `jwt` callback and `auth`, when using JWT sessions */\n  interface JWT {\n    /** OpenID ID Token */\n    idToken?: string\n  }\n}\n```\n\n<Callout type=\"info\">\n  The module declaration can be added to any file that is\n  [\"included\"](https://www.typescriptlang.org/tsconfig#include) in your\n  project&apos;s `tsconfig.json`.\n</Callout>\n\n## Resources\n\n1. [TypeScript documentation: Module Augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation)\n2. [DigitalOcean: Module Augmentation in TypeScript](https://www.digitalocean.com/community/tutorials/typescript-module-augmentation)\n3. [Creating a Database Adapter](/guides/creating-a-database-adapter)\n"
  },
  {
    "path": "docs/pages/global.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n@import url(\"./animated-stars.css\");\n\ndetails > *:not(summary) {\n  @apply p-4;\n}\n\nhtml[class~=\"dark\"]\n  :is(\n    img[src$=\"42-school.svg\"],\n    img[src$=\"apple.svg\"],\n    img[src$=\"boxyhq-saml.svg\"],\n    img[src$=\"eveonline.svg\"],\n    img[src$=\"github.svg\"],\n    img[src$=\"mailchimp.svg\"],\n    img[src$=\"medium.svg\"],\n    img[src$=\"okta.svg\"],\n    img[src$=\"patreon.svg\"],\n    img[src$=\"ping-id.svg\"],\n    img[src$=\"prisma.svg\"],\n    img[src$=\"resend.svg\"],\n    img[src$=\"roblox.svg\"],\n    img[src$=\"threads.svg\"],\n    img[src$=\"twitter.svg\"],\n    img[src$=\"wikimedia.svg\"]\n  ) {\n  filter: invert(1);\n}\n\n:is(html[class~=\"dark\"]) ::selection {\n  @apply bg-purple-500/40;\n}\n\n::selection {\n  @apply bg-purple-200/80;\n}\n\n/* Here because we can't safelist Nextra's tailwind classes\n* and this is used on the index.mdx page */\n._bg-primary-500 {\n  background-color: hsl(\n    var(--nextra-primary-hue) var(--nextra-primary-saturation) 50%\n  );\n}\n\n.underline-highlight {\n  position: relative;\n\n  &::after {\n    content: \"\";\n    position: absolute;\n    bottom: -0.5rem;\n    left: -0.5rem;\n    right: -0.5rem;\n    height: 0.9rem;\n    z-index: -1;\n    background-image: url(\"https://s3-us-west-2.amazonaws.com/s.cdpn.io/664131/underline.svg\");\n    background-repeat: no-repeat;\n    background-size: cover;\n  }\n}\n\n.gradient-bg {\n  background: url(/img/etc/rainbow.png);\n  background-size: cover;\n}\n\n.blur-shadow {\n  box-shadow: 6px -8px 10px rgb(250 245 255);\n}\n\n.button-primary {\n  background-color: rgb(147 51 234);\n  transition: background-color 0.2s ease-in-out;\n  box-shadow:\n    inset 0 0 0 2px #f3e5f529,\n    inset 0 -2px 1px #ce93d824,\n    inset 0 0 0 1px #6a1b9a36;\n}\n\n.button-primary:hover {\n  background-color: rgb(126 34 206);\n}\n\ndiv.nextra-code > pre {\n  @apply overflow-y-visible !bg-transparent dark:!bg-neutral-950;\n}\ndiv.nextra-code * pre {\n  --shiki-dark-bg: \"transparent\" !important;\n  @apply !bg-transparent;\n}\n\npre code.nextra-code:not(:has(span)) {\n  padding-inline: 1rem;\n}\n\n:is(html[class~=\"dark\"] .nextra-code span) {\n  color: var(--shiki-dark) !important;\n}\n\n@keyframes fadeIn {\n  from {\n    opacity: 0;\n    transform: scale(0);\n  }\n  to {\n    opacity: 1;\n    transform: scale(1);\n  }\n}\n\n@layer utilities {\n  .animation-delay-2000 {\n    animation-delay: 2s;\n  }\n  .animation-delay-4000 {\n    animation-delay: 4s;\n  }\n}\n\n@layer components {\n  .break-word-legacy {\n    word-break: break-word;\n  }\n}\n\n/* Scrollbar */\n::-webkit-scrollbar {\n  width: 0.5rem !important;\n  height: 0.4rem !important;\n}\n\n::-webkit-scrollbar-track {\n  border-radius: 100vh;\n  @apply !bg-transparent;\n}\n\n::-webkit-scrollbar-thumb {\n  @apply !bg-neutral-300 transition-colors duration-300 dark:!bg-neutral-700;\n  border-radius: 100vh;\n  transition: all 250ms ease-in-out;\n}\n\n::-webkit-scrollbar-thumb:hover {\n  @apply !bg-neutral-300 dark:!bg-neutral-700;\n  background-clip: unset !important;\n  cursor: pointer;\n}\n\n/* Navbar GitHub Star Counter */\n.github-counter {\n  position: absolute;\n  color: #000;\n  top: -5px;\n  right: -16px;\n  font-size: 9px;\n  background-color: #ccc;\n  padding: 2px 4px;\n  border-radius: 10px;\n  z-index: 1;\n  pointer-events: none;\n}\n\ndiv.nextra-search + a {\n  display: none;\n}\n\nhtml[data-theme=\"dark\"] .github-counter {\n  background-color: #222;\n  color: #fff;\n}\n\n/* Tooltip transitions */\n@keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n[data-scope=\"tooltip\"][data-part=\"content\"][data-state=\"open\"] {\n  animation: fadeIn 250ms ease-out;\n}\n\n[data-scope=\"tooltip\"][data-part=\"content\"][data-state=\"closed\"] {\n  animation: fadeOut 250ms ease-in;\n}\n\n.sponsoredBadge {\n  display: inline-block;\n  width: max-content;\n  font-size: 0.6rem;\n  border-radius: 2rem;\n  padding-inline: 0.5rem;\n  color: #696969;\n  @apply bg-neutral-200 dark:bg-neutral-950;\n}\n\narticle.nextra-content table {\n  overflow-x: auto;\n}\n\n/* Hide Nextra Search 'kbd' on small screen sizes */\ndiv.nextra-search kbd {\n  @apply !hidden lg:!flex;\n}\n\n/* Native popover */\n[popover] {\n  position: absolute;\n  top: calc(3rem + anchor(top));\n  left: anchor(implicit center);\n  translate: -50% 0;\n  position-try-options: flip-block, flip-inline;\n\n  /* Final state of the exit animation */\n  opacity: 0;\n  transition:\n    opacity 300ms,\n    transform 300ms,\n    overlay 300ms allow-discrete,\n    display 300ms allow-discrete;\n}\n\n[popover]:popover-open {\n  opacity: 1;\n}\n\n@starting-style {\n  [popover]:popover-open {\n    opacity: 0;\n  }\n}\n\n:is(html[class~=\"dark\"])\n  > head:has(meta[content*=\"reference/core/providers\"])\n  + body\n  .provider {\n  color: #e2e8f0 !important;\n  background-color: transparent !important;\n}\n\nhtml > head:has(meta[content*=\"reference/core/providers\"]) + body .provider {\n  padding: 1rem;\n  font-size: 1rem;\n  color: #000 !important;\n}\n\n.nextra-sidebar-container {\n  .DocSearch-Button {\n    @apply hidden;\n  }\n}\n"
  },
  {
    "path": "docs/pages/guides/_meta.js",
    "content": "export default {\n  debugging: \"Debugging\",\n  testing: \"Testing\",\n  pages: \"Pages\",\n  \"environment-variables\": \"Environment Variables\",\n  \"extending-the-session\": \"Extending the Session\",\n  \"restricting-user-access\": \"Restricting users accessing to the app\",\n  \"role-based-access-control\": \"Role-Based Access Control\",\n  \"corporate-proxy\": \"Supporting Corporate Proxies\",\n  \"edge-compatibility\": \"Edge Compatibility\",\n  \"configuring-github\": \"Configuring GitHub for OAuth\",\n  \"configuring-resend\": \"Configuring Resend for magic links\",\n  \"configuring-oauth-providers\": \"Configuring OAuth providers\",\n  \"configuring-http-email\": \"Configuring Custom HTTP Email Provider\",\n  \"creating-a-database-adapter\": \"Creating a Database Adapter\",\n  \"creating-a-framework-integration\": \"Creating a Framework Integration\",\n  \"refresh-token-rotation\": \"Refresh Token Rotation\",\n}\n"
  },
  {
    "path": "docs/pages/guides/configuring-github.mdx",
    "content": "import { Callout, Tabs } from \"nextra/components\"\nimport { Screenshot } from \"@/components/Screenshot\"\nimport { Code } from \"@/components/Code\"\n\n# OAuth with GitHub\n\nIn this tutorial, we'll be setting up Auth.js in a Next.js application to be able to log in with **GitHub**.\n\n<Callout type=\"info\">\n  This tutorial uses GitHub as the OAuth provider and Next.js as the framework.\n  Note that for any OAuth provider or any framework, **the process will be the\n  same/very similar**, mainly differing on how you register your application in\n  the chosen provider's dashboard.\n</Callout>\n\n## Setting up Auth.js\n\n### Installing Auth.js and Next.js\n\nFor this tutorial, we're gonna use the default [Auth.js & Next.js example app](https://github.com/nextauthjs/next-auth-example). If you already have an existing Next.js app, it should work too. If you don't, clone the repository:\n\n```bash\ngit clone https://github.com/nextauthjs/next-auth-example.git && cd next-auth-example\n```\n\nIf you're using the example app, Auth.js is already installed, otherwise follow the [installation instructions](/getting-started/installation).\n\n### Creating the server config\n\nNext, we're gonna create the main Auth.js configuration file which contains the necessary configuration for Auth.js, as well as the dynamic route handler.\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport GitHub from \"next-auth/providers/github\"\n\nexport const { handlers, auth } = NextAuth({\n  providers: [GitHub],\n})\n```\n\n```ts filename=\"./app/api/auth/[...nextauth]/route.ts\"\nimport { handlers } from \"@/auth\" // Referring to the auth.ts we just created\nexport const { GET, POST } = handlers\nexport const runtime = \"edge\" // optional\n```\n\nSince this is a [catch-all dynamic route](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes#catch-all-segments), it will respond to all the relevant Auth.js API routes so that your application can interact with the chosen OAuth provider using the [OAuth 2](https://oauth.net/2) protocol.\n\n### Adding environment variables\n\nIf you haven't, create an `.env.local` file as explained in the [installation section](/getting-started/installation) and add the following two GitHub variables:\n\n```bash filename=\".env.local\" {3-4}\nAUTH_SECRET=\"changeMe\"\n\nAUTH_GITHUB_ID=\nAUTH_GITHUB_SECRET=\n```\n\nWe will be filling `AUTH_GITHUB_ID` and `AUTH_GITHUB_SECRET` with proper values from the GitHub Developer Portal once we have registered our application in GitHub.\n\n## Registering your App\n\n### Creating an OAuth App in GitHub\n\nTo get the required credentials from GitHub, we need to create an application in their developer settings.\n\nGo to the [GitHub developer settings](https://github.com/settings/developers), also found under **Settings** → **Developers** → **OAuth Apps**, and click \"New OAuth App\":\n\nimport CreatingOAuthApp from \"../../public/img/oauth-setup/creating-oauth-app.webp\"\n\n<Screenshot src={CreatingOAuthApp} alt=\"Creating an OAuth App on GitHub\" />\n\nNext, you'll be presented with a screen to register your application. Fill in all the required fields.\n\nimport CallbackUrl from \"../../public/img/oauth-setup/callback-url.webp\"\n\n<Screenshot src={CallbackUrl} />\n\nThe default callback URL should generally take the form of `[origin]/api/auth/callback/[provider]`, however, the default is slightly different depending on which framework you're using.\n\n<Code>\n<Code.Next>\n\n```bash\n// Local\nhttp://localhost:3000/api/auth/callback/github\n\n// Prod\nhttps://app.company.com/api/auth/callback/github\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```bash\n// Local\nhttp://localhost:3000/auth/callback/github\n\n// Prod\nhttps://app.company.com/auth/callback/github\n```\n\nNotice no `/api` path parameter.\n\n</Code.Qwik>\n<Code.Svelte>\n\n```bash\n// Local\nhttp://localhost:3000/auth/callback/github\n\n// Prod\nhttps://app.company.com/auth/callback/github\n```\n\nNotice no `/api` path parameter.\n\n</Code.Svelte>\n<Code.Express>\n\n```bash\n// Local\nhttp://localhost:3000/auth/callback/github\n\n// Prod\nhttps://app.company.com/auth/callback/github\n```\n\nNotice no `/api` path parameter.\n\n</Code.Express>\n</Code>\n\nOnce you've entered all the required fields, press **\"Register application\"**.\n\n### Secrets\n\nAfter successfully registering your application, GitHub will present us with the required details.\n\nimport ClientIdSecret from \"../../public/img/oauth-setup/clientid-secret.webp\"\n\n<Screenshot src={ClientIdSecret} alt=\"Generating clientId and clientSecret\" />\n\nWe need 2 things from this screen, the **Client ID** and **Client Secret**.\n\nThe Client ID is always visible, it is a public identifier of your OAuth application within GitHub.\n\nTo get a Client Secret, you have to click on **\"Generate a new client secret\"**, which will create your first client secret. You can easily create a new client secret here in case your first one gets leaked, lost, etc.\n\n<Callout>\n  Keep your **Client Secret** secure and never expose it to the public or share\n  it with people outside your organization.\n</Callout>\n\n## Wiring all together\n\nNow that we have the required Client ID and Client Secret, paste them into your `.env.local` file we created earlier.\n\n```bash filename=\".env.local\" {3-4}\nAUTH_SECRET=\"changeMe\"\n\nAUTH_GITHUB_ID={clientId}\nAUTH_GITHUB_SECRET={clientSecret}\n```\n\nWith all the pieces in place, you can now start your local dev server and test the login process.\n\n```bash npm2yarn\nnpm run dev\n```\n\nNavigate to [`http://localhost:3000`](http://localhost:3000). You should see the following page:\n\nimport AppStart from \"../../public/img/oauth-setup/app-start.webp\"\n\n<Screenshot src={AppStart} alt=\"App Start\" />\n\nClick on **\"Sign in\"**, you should be redirected to the default Auth.js signin page. You can [customize this page](/guides/pages/signin) to fit your needs. Next, click on **\"Sign in with GitHub\"**. Auth.js will redirect you to GitHub, where GitHub will recognize your application and ask the user to confirm they want to authenticate to your new application by entering their credentials.\n\nimport GitHubCredentials from \"../../public/img/oauth-setup/github-auth-credentials.webp\"\n\n<Screenshot src={GitHubCredentials} alt=\"GitHub Credentials\" />\n\nOnce authenticated, GitHub will redirect the user back to your app and Auth.js will take care of the rest:\n\nimport GitHubAuthSuccess from \"../../public/img/oauth-setup/github-auth-success.webp\"\n\n<Screenshot src={GitHubAuthSuccess} alt=\"GitHub Authentication Success\" />\n\nIf you've landed back here that means everything worked! We have completed the whole OAuth authentication flow so that users can log in to your application via GitHub!\n\n<Callout type=\"info\">\n  As you can see, most of the time required setting up OAuth in your application\n  is spent registering your application in the OAuth provider's dashboard (some\n  are easier to navigate, some are harder). Once registered, the setup via\n  Auth.js should be straight forward.\n</Callout>\n\n## Deployment\n\nBefore you can release your app to production, you'll need to change a few things.\n\nUnfortunately, GitHub is among the providers which do not let you register multiple callback URLs for one application. Therefore, you'll need to register a separate application in GitHub's dashboard [as we did previously](/guides/configuring-github#registering-our-app) but set the callback URL to your application's production domain (.i.e `https://example.com/api/auth/callback/github`). You'll then also have a new **Client ID** and **Client Secret** that you need to add to your production environment via your hosting provider's dashboard (Vercel, Netlify, Cloudflare, etc.) or however you manage environment variables in production.\n\nRefer to the [Deployment page](/getting-started/deployment) for more information.\n"
  },
  {
    "path": "docs/pages/guides/configuring-http-email.mdx",
    "content": "---\ntitle: Configuring your own HTTP Email\n---\n\nimport { Code } from \"@/components/Code\"\n\n# HTTP Email\n\nWe have a few built-in HTTP Email providers like [Resend](/getting-started/providers/resend), [SendGrid](/getting-started/providers/sendgrid) and [Postmark](/getting-started/providers/postmark), sometimes you may want to use your own HTTP endpoint to send emails.\n\nTo do this, we can write our own provider with a custom [`sendVerificationRequest`](/reference/core/providers/email#sendverificationrequest) method. Don't forget, an `email` type provider **requires** a database adapter.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport { sendVerificationRequest } from \"./lib/authSendRequest\"\n\nexport const { handlers, auth } = NextAuth({\n  adapter,\n  providers: [\n    {\n      id: \"http-email\",\n      name: \"Email\",\n      type: \"email\",\n      maxAge: 60 * 60 * 24, // Email link will expire in 24 hours\n      sendVerificationRequest,\n    },\n  ],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { sendVerificationRequest } from \"../lib/authSendRequest\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      {\n        id: \"http-email\",\n        name: \"Email\",\n        type: \"email\",\n        maxAge: 60 * 60 * 24, // Email link will expire in 24 hours\n        sendVerificationRequest,\n      },\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport { sendVerificationRequest } from \"../lib/authSendRequest\"\n\nexport const { handle, auth } = SvelteKitAuth({\n  adapter,\n  providers: [\n    {\n      id: \"http-email\",\n      name: \"Email\",\n      type: \"email\",\n      maxAge: 60 * 60 * 24, // Email link will expire in 24 hours\n      sendVerificationRequest,\n    },\n  ],\n})\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```ts filename=\"./src/routes/auth.route.ts\"\nimport { ExpressAuth } from \"@auth/express\"\nimport { sendVerificationRequest } from \"../lib/authSendRequest\"\nimport express from \"express\"\n\nconst app = express()\n\napp.set(\"trust proxy\", true)\napp.use(\n  \"/auth/*\",\n  ExpressAuth({\n    adapter,\n    providers: [\n      {\n        id: \"http-email\",\n        name: \"Email\",\n        type: \"email\",\n        maxAge: 60 * 60 * 24, // Email link will expire in 24 hours\n        sendVerificationRequest,\n      },\n    ],\n  })\n)\n```\n\n</Code.Express>\n</Code>\n\nAfter we've setup the initial configuration, you've got to write `sendVerificationRequest` function. Below is a simple version which just sends a text email with a link to the user.\n\n```ts filename=\"./lib/authSendRequest.ts\"\nexport async function sendVerificationRequest({ identifier: email, url }) {\n  // Call the cloud Email provider API for sending emails\n  const response = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n    // The body format will vary depending on provider, please see their documentation\n    body: JSON.stringify({\n      personalizations: [{ to: [{ email }] }],\n      from: { email: \"noreply@company.com\" },\n      subject: \"Sign in to Your page\",\n      content: [\n        {\n          type: \"text/plain\",\n          value: `Please click here to authenticate - ${url}`,\n        },\n      ],\n    }),\n    headers: {\n      // Authentication will also vary from provider to provider, please see their docs.\n      Authorization: `Bearer ${process.env.SENDGRID_API}`,\n      \"Content-Type\": \"application/json\",\n    },\n    method: \"POST\",\n  })\n\n  if (!response.ok) {\n    const { errors } = await response.json()\n    throw new Error(JSON.stringify(errors))\n  }\n}\n```\n\nA more advanced `sendVerificationRequest` can be seen below, this is a version of the builtin function.\n\n```ts filename=\"./lib/authSendRequest.ts\"\nexport async function sendVerificationRequest(params) {\n  const { identifier: to, provider, url, theme } = params\n  const { host } = new URL(url)\n  const res = await fetch(\"https://api.resend.com/emails\", {\n    method: \"POST\",\n    headers: {\n      Authorization: `Bearer ${provider.apiKey}`,\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify({\n      from: provider.from,\n      to,\n      subject: `Sign in to ${host}`,\n      html: html({ url, host, theme }),\n      text: text({ url, host }),\n    }),\n  })\n\n  if (!res.ok)\n    throw new Error(\"Resend error: \" + JSON.stringify(await res.json()))\n}\n\nfunction html(params: { url: string; host: string; theme: Theme }) {\n  const { url, host, theme } = params\n\n  const escapedHost = host.replace(/\\./g, \"&#8203;.\")\n\n  const brandColor = theme.brandColor || \"#346df1\"\n  const color = {\n    background: \"#f9f9f9\",\n    text: \"#444\",\n    mainBackground: \"#fff\",\n    buttonBackground: brandColor,\n    buttonBorder: brandColor,\n    buttonText: theme.buttonText || \"#fff\",\n  }\n\n  return `\n<body style=\"background: ${color.background};\">\n  <table width=\"100%\" border=\"0\" cellspacing=\"20\" cellpadding=\"0\"\n    style=\"background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;\">\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        Sign in to <strong>${escapedHost}</strong>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\" style=\"padding: 20px 0;\">\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n          <tr>\n            <td align=\"center\" style=\"border-radius: 5px;\" bgcolor=\"${color.buttonBackground}\"><a href=\"${url}\"\n                target=\"_blank\"\n                style=\"font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;\">Sign\n                in</a></td>\n          </tr>\n        </table>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        If you did not request this email you can safely ignore it.\n      </td>\n    </tr>\n  </table>\n</body>\n`\n}\n```\n\nTo sign in via this custom provider, you would refer to it by the id in when you are calling the sign-in method, for example: `signIn('http-email', { email: 'user@company.com' })`.\n"
  },
  {
    "path": "docs/pages/guides/configuring-oauth-providers.mdx",
    "content": "import { Callout, Steps } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n# Configuring an OAuth provider\n\n## Override default provider config\n\nFor built-in providers, usually you only need to specify a client id and client secret, and in case of OIDC (OpenID Connect), an issuer as well.\nWe can [infer these from environment variables](/guides/environment-variables#oauth-variables).\n\nIf you need to override any of the defaults provider config options, you can add them in the provider's function call and they will be deeply-merged with our [defaults](https://github.com/nextauthjs/next-auth/tree/main/packages/core/src/providers).\nThat means you only have to override part of the options that you need to be different. For example if you want different scopes, overriding `authorization.params.scope` is enough, instead of the whole `authorization` option.\n\nFor example, to override a provider's default `scope`s, you can do the following:\n\n<Code>\n<Code.Next>\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\";\nimport Auth0 from \"next-auth/providers/auth0\";\n\nexport const { handlers, auth } = NextAuth({\n  providers: [\n    Auth0({ authorization: { params: { scope: \"openid custom_scope\" } } }),\n  ],\n});\n```\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Auth0 from \"@auth/qwik/providers/auth0\";\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Auth0({ authorization: { params: { scope: \"openid custom_scope\" } } }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n```ts filename=\"src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\";\nimport Auth0 from \"@auth/sveltekit/providers/auth0\";\n\nexport const { handle, signIn } = SvelteKitAuth({\n  providers: [\n    Auth0({ authorization: { params: { scope: \"openid custom_scope\" } } }),\n  ],\n});\n```\n\n```ts filename=\"src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n\nAnother example, the `profile` callback will return `name`, `email` and `picture` by default, but you might want to return more information from the provider. What you return will be used to create the user object in the database.\n\n<Code>\n<Code.Next>\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\";\nimport Auth0 from \"next-auth/providers/auth0\";\n\nexport const { handlers, auth } = NextAuth({\n  providers: [\n    Auth0({\n      // You can also make calls to external resources if necessary.\n      async profile(profile) {\n        return {};\n      },\n    }),\n  ],\n});\n```\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Auth0 from \"@auth/qwik/providers/auth0\";\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Auth0({\n        // You can also make calls to external resources if necessary.\n        async profile(profile) {\n          return {};\n        },\n      }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n```ts filename=\"src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\";\nimport Auth0 from \"@auth/sveltekit/providers/auth0\";\n\nexport const { handle } = SvelteKitAuth({\n  providers: [\n    Auth0({\n      // You can also make calls to external resources if necessary.\n      async profile(profile) {\n        return {};\n      },\n    }),\n  ],\n});\n```\n\n```ts filename=\"src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n\n## Use your own provider\n\n<Callout>\n  Check our [built-in OAuth providers](/getting-started/authentication/oauth)\n  first, before creating one from scratch.\n</Callout>\n\nWe support any [OAuth](https://datatracker.ietf.org/doc/html/rfc6749) or [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) compliant provider.\nStart by passing an object to the [`providers` list](/reference/core#providers):\n\n<Code>\n<Code.Next>\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\";\n\nexport const { handlers, auth } = NextAuth({\n  providers: [{\n    id: \"my-provider\", // signIn(\"my-provider\") and will be part of the callback URL\n    name: \"My Provider\", // optional, used on the default login page as the button text.\n    type: \"oidc\", // or \"oauth\" for OAuth 2 providers\n    issuer: \"https://my.oidc-provider.com\", // to infer the .well-known/openid-configuration URL\n    clientId: process.env.AUTH_CLIENT_ID, // from the provider's dashboard\n    clientSecret: process.env.AUTH_CLIENT_SECRET, // from the provider's dashboard\n  }],\n});\n```\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [{\n      id: \"my-provider\", // signIn(\"my-provider\") and will be part of the callback URL\n      name: \"My Provider\", // optional, used on the default login page as the button text.\n      type: \"oidc\", // or \"oauth\" for OAuth 2 providers\n      issuer: \"https://my.oidc-provider.com\", // to infer the .well-known/openid-configuration URL\n      clientId: import.meta.env.AUTH_CLIENT_ID, // from the provider's dashboard\n      clientSecret: import.meta.env.AUTH_CLIENT_SECRET, // from the provider's dashboard\n    }],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n```ts filename=\"src/auth.ts\"\nimport { SvelteKitAuth } from \"@auth/sveltekit\";\n\nexport const { handle } = SvelteKitAuth({\n  providers: [{\n    id: \"my-provider\", // signIn(\"my-provider\") and will be part of the callback URL\n    name: \"My Provider\", // optional, used on the default login page as the button text.\n    type: \"oidc\", // or \"oauth\" for OAuth 2 providers\n    issuer: \"https://my.oidc-provider.com\", // to infer the .well-known/openid-configuration URL\n    clientId: process.env.AUTH_CLIENT_ID, // from the provider's dashboard\n    clientSecret: process.env.AUTH_CLIENT_SECRET, // from the provider's dashboard\n  }],\n});\n```\n\n```ts filename=\"src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n\nThen, set the [callback URL](https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-07.html#name-client-redirection-endpoint) in your provider's dashboard to `https://app.com/{basePath}/callback/{id}`.\n\n<Callout type=\"info\">\n  By default, `basePath` is `/api/auth` for Next.js, and `/auth` in all other\n  integrations. See [`basePath`](/reference/core#basepath).\n</Callout>\n\nThat's it! 🎉\n\n## Adding a new built-in provider\n\nIf you think your custom provider might be useful to others, we encourage you to open a PR and add it to the built-in list.\n\n<Steps>\n\n### Creating the provider's file\n\nCreate a new `{provider}.ts` file under the [`packages/core/src/providers`](https://github.com/nextauthjs/next-auth/tree/main/packages/core/src/providers) directory.\n\n### Adhere to our code conventions\n\nUse the [built-in providers](https://github.com/nextauthjs/next-auth/tree/main/packages/core/src/providers) as a guide, make sure your provider adheres to the same code conventions, .i.e:\n\n- Use TypeScript\n- Use a named default export: `export default function YourProvider`\n- Export the TypeScript `interface` that defines the provider's available user info properties\n- Add the necessary JSDoc comments/documentation. For example, the [Auth0 provider](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/auth0.ts) is a good example for OIDC and the [GitHub Provider](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/github.ts) is an OAuth provider.)\n- Add links to the provider's API reference/documentation so others can understand how to set up this provider\n\n### Add your provider in the GitHub issues dropdown\n\nAdd the new provider name to the `Provider type` dropdown options in [`the provider issue template`](https://github.com/nextauthjs/next-auth/edit/main/.github/ISSUE_TEMPLATE/2_bug_provider.yml)\n\n### Add a logo\n\nAdd a logo `{provider}.svg` to the\n[`docs/static/img/providers`](https://github.com/nextauthjs/next-auth/tree/main/docs/static/img/providers) directory.\n\n</Steps>\n\nOnce the PR is merged, others will also be able to discover and use this provider with any of our integrations. That's it! 🎉\n"
  },
  {
    "path": "docs/pages/guides/configuring-resend.mdx",
    "content": "import { Callout, Steps, Tabs } from \"nextra/components\"\nimport { Screenshot } from \"@/components/Screenshot\"\n\n# Magic links with Resend\n\nIn this tutorial, we'll be setting up Auth.js in a Next.js application to be able to log in with **Resend**.\n\nMagic links (also known as \"passwordless\") authentication is a login method which uses emails containing a verification token embedded in a URL. When the user clicks on the link, they will be redirected to your Auth.js app and be logged in, as long as that verification token is still valid.\n\n<Callout type=\"info\">\n  This tutorial uses Resend as the Passwordless email provider and Next.js as\n  the framework. Note that for any OAuth provider or any framework, **the\n  process will be the same/very similar**, mainly differing on how you register\n  your application in the chosen provider's dashboard.\n</Callout>\n\n## Setting up Auth.js\n\n### Installing Auth.js and Next.js\n\nFor this tutorial, we're gonna use the default [Auth.js & Next.js example app](https://github.com/nextauthjs/next-auth-example). If you already have an existing Next.js app, it should work too. If you don't, clone the repository:\n\n```bash\ngit clone https://github.com/nextauthjs/next-auth-example.git && cd next-auth-example\n```\n\nIf you're using the example app, Auth.js is already installed, otherwise follow the [installation instructions](/getting-started/installation).\n\n### Creating the server config\n\nNext, we're gonna create the main Auth.js configuration file which contains the necessary configuration for Auth.js, as well as the dynamic route handler.\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Resend from \"next-auth/providers/resend\"\n\nexport const { handlers, auth } = NextAuth({\n  providers: [Resend],\n})\n```\n\n```ts filename=\"app/api/auth/[...nextauth]/route.ts\"\nexport { GET, POST } from \"@/auth\"\n```\n\nSince this is a [catch-all dynamic route](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes#catch-all-segments), it will respond to all the relevant Auth.js API routes so that your application can interact with the chosen OAuth provider using the [OAuth 2](https://oauth.net/2) protocol.\n\n### Adding environment variables\n\nIf you haven't, create an `.env.local` file as explained in the [installation section](/getting-started/installation) and add the following Resend API key variable.\n\n```bash filename=\".env.local\" {3}\nAUTH_SECRET=\"changeMe\"\n\nAUTH_RESEND_KEY=\n```\n\nWe will be filling `AUTH_RESEND_KEY` with a proper key from the Resend developer portal once we've registered our account and application.\n\n## Registering your App\n\nTo be able to send Emails using Resend you'll need to do two things.\n\n1. Create an API Key\n2. Verify your Domain\n\n### API Key\n\nYou'll need to sign up for an account at [Resend](https://resend.com), and then go to [\"API Keys\"](https://resend.com/api-keys) in the main sidebar. There you can click on **\"Create API Key\"**. We only need \"Sending Access\".\n\n### Domain\n\nTo verify your domain, follow the [Resend docs](https://resend.com/docs/dashboard/domains/introduction) and come back once you've got everything set up with your domain.\n\nNext, you will have to update the `from` address to be from the domain you've configured and verified in Resend.\n\n```ts\nimport NextAuth from \"next-auth\"\nimport Resend from \"next-auth/providers/resend\"\n\nexport const { handlers, auth } = NextAuth({\n  providers: [\n    Resend({\n      from: \"auth@app.company.com\",\n    }),\n  ],\n})\n```\n\n## Wiring all together\n\nNow that we have the required API key, paste it into your `.env.local` file we created earlier.\n\n```bash filename=\".env.local\" {3}\nAUTH_SECRET=\"changeMe\"\n\nAUTH_RESEND_KEY={apiKey}\n```\n\nWith all the pieces in place, you can now start your local dev server and test the login process.\n\n```bash npm2yarn\nnpm run dev\n```\n\nNavigate to [`http://localhost:3000`](http://localhost:3000). You should see the following page:\n\nimport AppStart from \"../../public/img/oauth-setup/app-start.webp\"\n\n<Screenshot src={AppStart} alt=\"App Start\" />\n\nClick on **\"Sign in\"**, you should be redirected to the default Auth.js signin page. You can [customize this page](/guides/pages/signin) to fit your needs. Next, enter your email address in the email input field and click **\"Sign in with Resend\"**.\n\nGo to your email inbox and you should find the email from your Auth.js application with a button labelled \"Sign in\". Click on this button and you should be redirected back to your local dev app and be signed in!\n\nimport GitHubAuthSuccess from \"../../public/img/oauth-setup/github-auth-success.webp\"\n\n<Screenshot src={GitHubAuthSuccess} alt=\"GitHub Authentication Success\" />\n\nIf you've landed back here that means everything worked! We have completed the whole passwordless authentication flow so that your users can log in to your application via passwordless magic-links!\n\n<Callout>\n  You can customize the contents of this email and modify some additional Resend\n  parameters. For more details, check out our [Resend\n  provider](/getting-started/providers/resend) docs page.\n</Callout>\n\n## Deployment\n\nDeploying your Auth.js application with Resend does not require any other changes. Just make sure you've added all the required environment variables to your production environment. Refer to the [Deployment page](/getting-started/deployment) for more information.\n"
  },
  {
    "path": "docs/pages/guides/corporate-proxy.mdx",
    "content": "import { Code } from \"@/components/Code\"\n\n# Supporting corporate proxies\n\nAuth.js libraries use the `fetch` API to communicate with OAuth providers. If your organization uses a corporate proxy, you may need to configure the `fetch` API to use the proxy.\n\n## Using a custom fetch function\n\nYou can provide a custom `fetch` function by passing it as an option to the provider.\n\n# Using Undici Library\n\nHere, we use the `undici` library to make requests through a proxy server, by passing a `dispatcher` to the `fetch` implementation by `undici`.\n\n<Code>\n  <Code.Next>\n\n```tsx filename=\"auth.ts\"\nimport NextAuth, { customFetch } from \"next-auth\"\nimport GitHub from \"next-auth/providers/github\"\nimport { ProxyAgent, fetch as undici } from \"undici\"\n\nconst dispatcher = new ProxyAgent(\"my.proxy.server\")\nfunction proxy(...args: Parameters<typeof fetch>): ReturnType<typeof fetch> {\n  // @ts-expect-error `undici` has a `duplex` option\n  return undici(args[0], { ...args[1], dispatcher })\n}\n\nexport const { handlers, auth } = NextAuth({\n  providers: [GitHub({ [customFetch]: proxy })],\n})\n```\n\n  </Code.Next>\n</Code>\n\n# Using HttpsProxyAgent\n\nOn Edge Runtimes or with proxy restrictions, the `undici` library may not work. Using a simpler approach with HttpsProxyAgent by passing a `proxyAgent` to the `fetch` implementation.\n\n<Code>\n  <Code.Next>\n\n```tsx filename=\"auth.ts\"\nimport NextAuth, { customFetch } from \"next-auth\"\nimport GitHub from \"next-auth/providers/github\"\nconst { HttpsProxyAgent } = require(\"https-proxy-agent\")\n\nconst proxyAgent = new HttpsProxyAgent(\"my.proxy.server\")\nasync function proxy(url: string, options: any): Promise<Response> {\n  const response = (await fetch(url, {\n    ...options,\n    agent: proxyAgent,\n  })) as unknown as Response\n  return response\n}\n\nexport const { handlers, auth } = NextAuth({\n  providers: [GitHub({ [customFetch]: proxy })],\n})\n```\n\n  </Code.Next>\n</Code>\n\n## Resources\n\n- [`undici` - Basic Proxy Request with local agent dispatcher](https://undici.nodejs.org/#/docs/api/ProxyAgent?id=example-basic-proxy-request-with-local-agent-dispatcher)\n"
  },
  {
    "path": "docs/pages/guides/creating-a-database-adapter.mdx",
    "content": "import { Callout } from \"nextra/components\"\n\n# Creating a database adapter\n\nAuth.js adapters allow you to integrate with any (even multiple) database/back-end service, even if we don't have an [official package](https://github.com/nextauthjs/next-auth/tree/main/packages) available yet. (We welcome PRs for new adapters! See the [guidelines](#official-adapter-guidelines) below.)\n\nAuth.js adapters are very flexible, and you can implement only the methods you need, and only create the database tables/columns that are actually going to be used.\n\nAn Auth.js adapter is a function that receives an ORM/database client and returns an object with methods (based on the [`Adapter` interface](/reference/core/adapters#adapter)) that interact with the database. The same database Adapter will be compatible with any Auth.js library.\n\nOptionally, you can run our [Adapter tests](https://github.com/nextauthjs/next-auth/blob/main/packages/utils/adapter.ts) on your adapter to ensure it is compliant with the Auth.js.\n\n## User management\n\nAuth.js differentiates between users and accounts. A user can have multiple accounts. An account is created for each provider type the user signs in with for the first time. For example, if a user signs in with Google and then with Facebook, they will have two accounts, one for each provider. The first provider the user signs in with will also be used to create the user object. See the [`profile()` provider method](/reference/core/providers#profile).\n\n### Methods and models\n\n<div style={{display: \"grid\", gridTemplateColumns: \"1fr 1fr\"}}>\n  <span>\n    - [`createUser`](/reference/core/adapters#createuser)\n    - [`getUser`](/reference/core/adapters#getuser)\n    - [`getUserByAccount`](/reference/core/adapters#getuserbyaccount)\n    - [`updateUser`](/reference/core/adapters#updateuser)\n    - [`linkAccount`](/reference/core/adapters#linkaccount)\n\n    Not yet invoked by Auth.js:\n    - [_`deleteUser`_](/reference/core/adapters#deleteuser)\n    - [_`unlinkAccount`_](/reference/core/adapters#unlinkaccount)\n\n  </span>\n  ```mermaid\n  erDiagram\n      User ||--|{ Account : \"\"\n      User {\n        string id\n      }\n      Account {\n        string userId\n        string type\n        string provider\n        string providerAccountId\n      }\n  ```\n</div>\n\nSee also: [User](/concepts/database-models#user) and [Account](/concepts/database-models#account) models.\n\n<Callout type=\"info\">\n  Although Auth.js doesn't require it, for basic display purposes, we recommend\n  adding the following columns to the `User` table as well: `name`, `email`,\n  `image`. You can configure the columns via the [`profile()` provider\n  method](/reference/core/providers#profile). If you don't need to save these\n  properties, create an empty `profile() {}` method.\n</Callout>\n\n<Callout type=\"info\">\n  Although Auth.js doesn't require it, the `Account` table typically saves\n  tokens retrieved from the provider. You can configure the columns via the\n  [`account()` provider method](/reference/core/providers#account). If you don't\n  need to save tokens, create an empty `account() {}` method.\n</Callout>\n\n## Database session management\n\nAuth.js can manage sessions in two ways. Learn about them and their advantages and disadvantages at [Concepts: Session strategies](/concepts/session-strategies).\n\n### Methods and models\n\n<div style={{display: \"grid\", gridTemplateColumns: \"1fr 1fr\"}}>\n  <span>\n    - [`createSession`](/reference/core/adapters#createsession)\n    - [`getSessionAndUser`](/reference/core/adapters#getsessionanduser)\n    - [`updateSession`](/reference/core/adapters#updatesession)\n    - [`deleteSession`](/reference/core/adapters#deletesession)\n  </span>\n  ```mermaid\n  erDiagram\n      User ||--|{ Account : \"\"\n      User {\n        string id\n      }\n      User ||--|{ Session : \"\"\n      Session {\n        string id\n        timestamp expires\n        string sessionToken\n        string userId\n      }\n      Account {\n        string userId\n        string type\n        string provider\n        string providerAccountId\n      }\n  ```\n</div>\n\nIf you want to use database sessions, you will need to implement the following methods:\n\nTo add database session management, you will need to expand your database tables/columns as follows:\n\nSee also: [Session](/concepts/database-models#session) models.\n\n## Verification tokens\n\nWhen you want to support email/passwordless login, Auth.js uses a database to store temporary verification tokens that are tied to a user's email address.\n\n### Methods and models\n\n<div style={{display: \"grid\", gridTemplateColumns: \"1fr 1fr\"}}>\n  <span>\n    - [`getUserByEmail`](/reference/core/adapters#getuserbyemail)\n    - [`createVerificationToken`](/reference/core/adapters#createverificationtoken)\n    - [`useVerificationToken`](/reference/core/adapters#useverificationtoken)\n  </span>\n  ```mermaid\n  erDiagram\n      User ||--|{ Account : \"\"\n      User {\n        string id\n        timestamp emailVerified\n      }\n      Account {\n        string userId\n        string type\n        string provider\n        string providerAccountId\n      }\n      User ||--|{ VerificationToken : \"\"\n      VerificationToken {\n        string identifier\n        string token\n        timestamp expires\n      }\n  ```\n</div>\n\nSee also: [Verification Token](/concepts/database-models#verification-token) models.\n\n## Official adapter guidelines\n\n<Callout type=\"info\">\n  When all of the below steps are done, you are ready to submit a PR to our\n  [repository](https://github.com/nextauthjs/next-auth).\n</Callout>\n\nIf you created an adapter and want us to distribute it as an official package, please make sure it meets the following requirements. Check out this [existing adapter](https://github.com/nextauthjs/next-auth/tree/main/packages/adapter-prisma) to learn about the package structure, required files, test setup, config, etc.\n\n1. The Adapter _must_ implement all methods of the [`Adapter` interface](/reference/core/adapters#adapter)\n1. [Adapter tests](https://github.com/nextauthjs/next-auth/blob/main/packages/utils/adapter.ts) _must_ be included and _must_ pass. Docker is favored over services, to make CI resilient to network errors and to reduce the number of GitHub Action Secrets (which also lets us run these tests in fork PRs)\n1. The Adapter _must_ follow these coding styles\n\n   - Written in TypeScript\n   - Passes the linting rules of the monorepo\n   - Does not include polyfills\n   - Configured as an ES module (ESM)\n   - Documented via JSDoc comments\n   - Have at least one named export exported from its main module. (For example `export function MyAdapter(): Adapter {}`)\n   - collection/table names should follow the convention (plural/singular, camelCase/snake_case) of the underlying ORM/database docs/conventions\n\n1. Configure the monorepo to help us maintain the package\n\n   - Add a (preferably `.svg`) logo to [this directory](https://github.com/nextauthjs/next-auth/tree/main/docs/public/img/adapters)\n   - Add the Adapter to our GitHub workflow files [here](https://github.com/nextauthjs/next-auth/tree/main/.github/workflows/release.yml#L12) and [here](https://github.com/nextauthjs/next-auth/tree/main/.github/pr-labeler.yml)\n   - Make sure to [`.gitignore` generated files](https://github.com/nextauthjs/next-auth/tree/main/.gitignore#L58) if there are any\n\n1. The Adapter _must_ be able to handle any property coming from the user\n\n   ORMs/database clients might have their own data types, but Auth.js expects these to be normalized as plain JavaScript objects for consistency. If your ORM/database client does not convert automatically, you need to convert the values when reading/writing from/to the database.\n\n   You might be tempted to check the name of a property and convert it based on that, but this is not scalable (eg.: a `User` object might have more than one `Date` property, not only `emailVerified`).\n\n   Instead, we recommend creating util functions that convert the values. Below is an example of how to convert dates (if your ORM/database client uses other data types, remember to convert them too, not only dates). It checks if the value can be parsed as a date, and if so, it converts it to a `Date` object. Otherwise, it leaves the original value as is.:\n\n```ts\n// https://github.com/honeinc/is-iso-date/blob/8831e79b5b5ee615920dcb350a355ffc5cbf7aed/index.js#L5\nconst isoDateRE =\n  /(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d:[0-5]\\d|Z))|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))/\n\nconst isDate = (val: any): val is ConstructorParameters<typeof Date>[0] =>\n  !!(val && isoDateRE.test(val) && !isNaN(Date.parse(val)))\n\nexport const format = {\n  /** Takes an object that's coming from a database and converts it to plain JavaScript. */\n  from<T>(object: Record<string, any> = {}): T {\n    const newObject: Record<string, unknown> = {}\n    for (const [key, value] of Object.entries(object))\n      if (isDate(value)) newObject[key] = new Date(value)\n      else newObject[key] = value\n    return newObject as T\n  },\n  /** Takes an object that's coming from Auth.js and prepares it to be written to the database. */\n  to<T>(object: Record<string, any>): T {\n    const newObject: Record<string, unknown> = {}\n    for (const [key, value] of Object.entries(object))\n      if (value instanceof Date) newObject[key] = value.toISOString()\n      else newObject[key] = value\n    return newObject as T\n  },\n}\n```\n\n## TypeScript\n\nYou can take advantage of the types that comes with the framework packages (i.e. `next-auth/adapters`, `@auth/sveltekit/adapters`).\n\n```ts\nimport type { Adapter } from \"next-auth/adapters\"\n\nfunction MyAdapter(): Adapter {\n  return {\n    // your adapter methods here\n  }\n}\n```\n\nWhen writing your Adapter in JavaScript, you can still use JSDoc to get helpful editor hints and auto-completion.\n\n```js {1}\n/** @return { import(\"next-auth/adapters\").Adapter } */\nfunction MyAdapter() {\n  return {\n    // your adapter methods here\n  }\n}\n```\n\n## Resources\n\n- [Official adapters' source code](https://github.com/nextauthjs/next-auth/tree/main/packages)\n- [`Adapter` interface](/reference/core/adapters#adapter)\n"
  },
  {
    "path": "docs/pages/guides/creating-a-framework-integration.mdx",
    "content": "# Creating a Framework Integration\n\nThe core functionalities of Auth.js - `@auth/core` - are built on top of the Web Standard [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request)/[Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) mental model, and therefore are framework-agnostic. For each framework, we provide an integration layer that allows you to use the authentication features in a way that is specific to the framework. See the [list of integrations](/getting-started/integrations) that are currently available.\n\nWe welcome contributions of new official integrations. If you are interested in creating & maintaining a new integration, please read the following guidelines.\n\n## Official framework guidelines\n\nIf you want to create a new official framework integration and distribute it under `@auth/` namespace, please make sure to follow the next steps and fulfill our maintenance requirements below:\n\n### Setting up a new integration\n\nWe provide a script that generates all the required files for a new integration. To run the script, run the following command:\n\n```bash npm2yarn\nnpm setup-fw-integration <framework-name>\n```\n\nThis will copy all the files from our official template at `./packages/frameworks-template` to a new directory under `packages/` with all the required files, and rename the placeholders to the name of the framework you provided.\n\n- Coding styles. The source code files should:\n\n  - Be written in TypeScript\n  - Pass the linting rules of the monorepo\n  - Have a named export exported from its main module. For example: `export function ExpressAuth()`\n\n- API reference documentation - We use [TypeDoc](https://typedoc.org/) for automated documentation generation. The documentation should:\n\n  - Be written in JSDoc comments.\n  - Explain how to use and configure the integration: How to do Session management, how to login/logout, how to configure the base URL.\n  - Include a link to the official framework documentation in the reference section.\n\n- Deployed Example. This task can be in a follow-up PR. The deployed example should:\n\n  - All the actions (URL) should work as expected.\n  - Have at least one OAuth provider configured.\n  - The example code should live under `apps/examples/<framework-name>`. For example: `apps/examples/express`.\n\nThe above are required for us to distribute the package as an official package. Once the checklist is completed, you can mark the PR as \"Ready for review\" and the maintainers will review it.\n"
  },
  {
    "path": "docs/pages/guides/debugging.mdx",
    "content": "import { Callout } from \"nextra/components\"\n\n# Debugging\n\nDebugging Auth.js starts with enabling the `debug` option in your main Auth.js configuration.\n\n```ts filename=\"./auth.ts\" {4}\nimport NextAuth from \"next-auth\"\n\nexport const { handlers, auth } = NextAuth({\n  debug: true,\n})\n```\n\nThis will use the `console` methods to log out many details about the authentication process, including requests, responses, errors, and database requests and responses.\n\n## Logging\n\nYou can customize the logging output by providing your own logger. This is useful if you want to send logs to a logging service, or if you want to customize the format of the logs.\n\n```ts filename=\"./auth.ts\"\nimport log from \"logging-service\"\nexport const { handlers, auth } = NextAuth({\n  logger: {\n    error(code, ...message) {\n      log.error(code, message)\n    },\n    warn(code, ...message) {\n      log.warn(code, message)\n    },\n    debug(code, ...message) {\n      log.debug(code, message)\n    },\n  },\n})\n```\n\n<Callout type=\"warning\">\n  Enabling the `debug` option in production can lead to sensitive information\n  being saved in your logs. Make sure to sanitize any sensitive information.\n</Callout>\n\nWhen `logger` options are set, the `debug` option is ignored\n"
  },
  {
    "path": "docs/pages/guides/edge-compatibility.mdx",
    "content": "import { Callout } from \"nextra/components\"\n\nAs Edge runtimes become more and more popular people are naturally trying to deploy Auth.js and `next-auth` in these environments and are running into some fundamental compatibility issues that plague the entire ecosystem at the moment. We're hoping with this document we can pick people up no matter where they currently are in terms of understanding and experience and help them understand the challenges and hopefully get Auth.js up and running in whichever runtime they choose!\n\nTo begin, let us get some background knowledge out of the way. If you're familiar with this, feel free to skip this section!\n\n## Definitions\n\nWe're going to be talking specifically about Auth.js and how it intersects with the [edge runtimes](https://runtime-compat.unjs.io) that are very popular today with various frameworks, hosting providers, libraries, etc.\n\nFirst things first, **what is \"edge\"** in this context? Edge here is borrowed from the network engineering folks and refers to a compute node (i.e. server) that is located on the edge of a network, i.e. closer to the users. Usually these are compute nodes that are lower power than the kind of full-fledged servers that can be found in the core of a datacenter that run most important workloads. Some advantages of running code here include lower latency to the users end devices, better scalability story, and more cost-effective compute. Some disadvantages include less powerful hardware and potentially different compatibility in terms of the software stack.\n\nSo when we say <abbr title=\"The JavaScript execution environment usually running at the edge of a network, closer to the user, that is explicitly NOT Node.js and is therefore different in various aspects compared to what we're used to running our code with (i.e. Node.js).\">**edge runtimes**</abbr>, we mean a server-side JavaScript runtime that is **not** Node.js and is optimized to run on these edge compute nodes (servers). That generally means that the code is executing closer to your users on lower power hardware that is optimized for other things like quick startup times, low memory usage, etc.\n\nThis is a problem because these runtimes are often missing features that Node.js has and sometimes these are critical to the functioning of the libraries and packages you rely on. When a package says it's \"edge compatible\" or \"edge ready\", what they really mean is that they've engineered their software to avoid any of the Node.js features / modules that are missing in some of the edge runtimes, thereby making them more universally compatible. Check out unjs's [compatibility matrix](https://runtime-compat.unjs.io) to get an idea of which runtimes support which features. While not critical to Auth.js, this is a good time to mention that there is an industry group designed to provide a space for JavaScript runtimes to collaborate on API interop - [WinterCG](https://wintercg.org).\n\n<Callout>\n  I want to note here that these features / modules are often missing because\n  the underlying environment they're running on doesn't provide them. For\n  example, developers can invest as much time as they want, but if their\n  server-side JavaScript runtime is going to be running in a sandboxed operating\n  system environment that doesn't give them access to the Filesystem, then they\n  won't be able to implement the `fs` module no matter how hard they try.\n</Callout>\n\nBecause this Node.js vs. other runtimes situation is so fragmented and fluid at the moment, many libraries are optimizing their workloads to use only the most common denominator features, like `fetch`. For example, if you're a database provider and you can engineer your system so that your client library only has to make HTTP requests to communicate with your backend, then you can advertise your library as \"edge compatible\" and run in any place your users may want to. This is as opposed to other database client libraries which have to use raw TCP sockets from Node.js to communicate with their backend, for example.\n\n## Auth.js\n\nEdge compatibility is something Auth.js has optimized for. That means that you can run the core Auth.js functionality on any JavaScript runtime you choose. The key word here, however, being **core functionality**. If you use _only_ Auth.js / `next-auth` and no other library in your Auth.js callbacks, Proxy, etc. then you can use it wherever you want!\n\nIssues begin to arise when you want to use other libraries with Auth.js.\n\n## The Problem\n\n### Database Adapters\n\nA common package to pair with Auth.js to implement a holistic authentication system is a database client. Database clients are troublesome in that they often leverage TCP sockets to communicate directly with the database server. One such common database which does this is PostgreSQL.\n\nPostgreSQL is a database that uses a message-based protocol for communication between a the client and server that is transported via **TCP (or Unix) sockets**. Raw TCP sockets are one of those Node.js features that are generally not available to edge runtimes. Therefore, on the surface, it seems like it's not possible to communicate with a PostgreSQL database from JavaScript running on edge runtime. The same goes for many other databases and their respective communication protocols.\n\nAs edge runtimes have matured and become more popular, however, people have gotten creative and implemented various solutions to this problem. One such common solution is to put some sort of API server in front of the database whose goal is to translate database queries sent to it via HTTP into a protocol the database can understand. This allows the client side to only have to make HTTP requests to the API server, which is something that every edge runtime supports.\n\n### Proxy (formerly Middleware)\n\nIn Next.js and `next-auth` you can also use Next.js [Proxy](https://nextjs.org/docs/app/api-reference/file-conventions/proxy) (formerly [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware)) to protect routes by checking if a session exists and deciding where to route next.\n\n<Callout>\n  As of Next.js 16, `middleware.ts` has been renamed to `proxy.ts` and now runs\n  on the **Node.js runtime** instead of the edge runtime. If you are using\n  Next.js 16+, the edge compatibility workarounds below may no longer be\n  necessary.\n</Callout>\n\nFor older versions of Next.js, **Middleware code always runs in an edge runtime**. This means that our code will be trying to execute, for example, PostgreSQL queries in an environment where the underlying functionality is not available (i.e. TCP sockets). Therefore, **to use a database adapter that isn't explicitly \"edge compatible\", we will need to find a way to query the database using the features that we do have available to us**.\n\n## The Solution\n\nAuth.js used with the [database session strategy](/concepts/session-strategies#database-session) and a database adapter makes many calls to the database during normal operations. No matter which framework you're using, every Auth.js client can fetch the currently active session and this is done by querying the database to check if the user's `sessionToken` is both in the database and valid (i.e. not expired).\n\nThis means that everywhere in your application where you may want to check if the user is authenticated or not will require a database call. Now in real life Auth.js is a bit smarter about this and uses caching and other tricks to avoid unnecessary database requests, but you can imagine that every `auth()` call will trigger a database query. Therefore, we need some sort of workaround to use Auth.js in edge runtimes with many database adapters!\n\n### Split Config\n\nWith Next.js and `next-auth` in mind, let's think about what we need to do to make Auth.js be able to both run some of its code in an edge runtime, but also use a database to store its sessions. We would need a separate \"version\" of `next-auth` without the database settings for the edge environment and another one with the database for everywhere else. To achieve this, we can use the [\"lazy initialization\"](/reference/nextjs#lazy-initialization) features of Auth.js to instantiate a standalone client without the adapter for the proxy and another one to be used everywhere else.\n\n1. First, a common Auth.js configuration object to be used everywhere. This **will not** include the database adapter.\n\n```ts filename=\"auth.config.ts\" /NextAuthConfig/\nimport GitHub from \"next-auth/providers/github\"\nimport type { NextAuthConfig } from \"next-auth\"\n\n// Notice this is only an object, not a full Auth.js instance\nexport default {\n  providers: [GitHub],\n} satisfies NextAuthConfig\n```\n\n2. Next, a separate **instantiated** Auth.js instance which imports that configuration, but also adds the adapter and using `jwt` for the Session strategy:\n\n```ts filename=\"auth.ts\" {2, 10, 11}\nimport NextAuth from \"next-auth\"\nimport authConfig from \"./auth.config\"\n\nimport { PrismaClient } from \"@prisma/client\"\nimport { PrismaAdapter } from \"@auth/prisma-adapter\"\n\nconst prisma = new PrismaClient()\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  adapter: PrismaAdapter(prisma),\n  session: { strategy: \"jwt\" },\n  ...authConfig,\n})\n```\n\n3. Our Proxy (or Middleware for older Next.js versions), which would then import the configuration **without the database adapter** and instantiate its own Auth.js client.\n\n```ts filename=\"proxy.ts\" {2, 4}\nimport NextAuth from \"next-auth\"\nimport authConfig from \"./auth.config\"\n\nexport const { auth: proxy } = NextAuth(authConfig)\n```\n\n4. Finally, everywhere else we can import from the primary `auth.ts` configuration and use `next-auth` as usual. See our [session management docs](/getting-started/session-management/protecting) for more examples.\n\n```tsx filename=\"app/protected/page.tsx\" {4} /session/\nimport { auth } from \"@/auth\"\n\nexport default async function Page() {\n  const session = await auth()\n\n  if (!session) {\n    return <div>Not authenticated</div>\n  }\n\n  return (\n    <div className=\"container\">\n      <pre>{JSON.stringify(session, null, 2)}</pre>\n    </div>\n  )\n}\n```\n\nIt is important to note here that we've now removed database functionality and support from `next-auth` **in the proxy**. That means that we won't be able to fetch the session or other info like the user's account details, etc. while executing code in the proxy. That means you'll want to rely on checks like the one demonstrated above in the `/app/protected/page.tsx` file to ensure you're [protecting your routes](/getting-started/session-management/protecting) effectively. The proxy is then still used for bumping the session cookie's expiry time, for example.\n"
  },
  {
    "path": "docs/pages/guides/environment-variables.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n# Environment variables\n\n## Auth secret\n\n<Code>\n<Code.Next>\n\n```bash filename=\".env.local\"\nAUTH_SECRET=\"This is an example\"\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```bash filename=\".env\"\nAUTH_SECRET=\"This is an example\"\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```bash filename=\".env\"\nAUTH_SECRET=\"This is an example\"\n```\n\n</Code.Svelte>\n<Code.Express>\n\n```bash filename=\".env\"\nAUTH_SECRET=\"This is an example\"\n```\n\n</Code.Express>\n</Code>\n\n`AUTH_SECRET` is a random token used by the library to encrypt tokens and email verification hashes, and it's mandatory to keep things secure (See [Deployment](/getting-started/deployment) to learn more). You can use the CLI to generate an auth secret:\n\n```bash npm2yarn\nnpm exec auth secret\n```\n\n## Environment Variable Inference\n\nAuth.js is automatically configured to pick the right environment variables for `clientId` and `clientSecret` when using an [official OAuth provider](/getting-started/authentication/oauth).\n\nThe shape of these variables in your `.env` files should always follow the same pattern:\n\n```\nAUTH_[PROVIDER]_ID=\nAUTH_[PROVIDER]_SECRET=\n```\n\nFor example if we're using the Google, Twitter and GitHub providers, your `.env` file would look something like this.\n\n```bash\n# Google\nAUTH_GOOGLE_ID=123\nAUTH_GOOGLE_SECRET=123\n\n# Twitter\nAUTH_TWITTER_ID=123\nAUTH_TWITTER_SECRET=123\n\n# GitHub\nAUTH_GITHUB_ID=123\nAUTH_GITHUB_SECRET=123\n```\n\nThen in your Auth.js configuration file, the `provider` array is simplified to this.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\" {7}\nimport NextAuth from \"next-auth\"\nimport Google from \"next-auth/providers/google\"\nimport Twitter from \"next-auth/providers/twitter\"\nimport GitHub from \"next-auth/providers/github\"\n\nexport const { handlers, auth } = NextAuth({\n  providers: [Google, Twitter, GitHub],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Google from \"@auth/qwik/providers/google\"\nimport Twitter from \"@auth/qwik/providers/twitter\"\nimport GitHub from \"@auth/qwik/providers/github\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [Google, Twitter, GitHub],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./auth.ts\" {7}\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport Google from \"@auth/sveltekit/providers/google\"\nimport Twitter from \"@auth/sveltekit/providers/twitter\"\nimport GitHub from \"@auth/sveltekit/providers/github\"\n\nexport const { handle } = SvelteKitAuth({\n  providers: [Google, Twitter, GitHub],\n})\n```\n\n</Code.Svelte>\n</Code>\n\nIf for some reason you want to name the variables differently:\n\n```bash\n# Google\nAUTH_WEBAPP_GOOGLE_CLIENT_ID=123\nAUTH_WEBAPP_GOOGLE_CLIENT_SECRET=123\n```\n\nThen you will need to manually reference them in the config:\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\" {7-8}\nimport NextAuth from \"next-auth\"\nimport Google from \"next-auth/providers/google\"\n\nexport const { handlers, auth } = NextAuth({\n  providers: [\n    Google({\n      clientId: process.env.AUTH_WEBAPP_GOOGLE_CLIENT_ID,\n      clientSecret: process.env.AUTH_WEBAPP_GOOGLE_CLIENT_SECRET,\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Google from \"@auth/qwik/providers/google\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Google({\n        clientId: import.meta.env.AUTH_WEBAPP_GOOGLE_CLIENT_ID,\n        clientSecret: import.meta.env.AUTH_WEBAPP_GOOGLE_CLIENT_SECRET,\n      }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\" {8-9}\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport Google from \"@auth/sveltekit/providers/google\"\nimport { env } from \"$env/dynamic/private\"\n\nexport const { handle } = SvelteKitAuth({\n  providers: [\n    Google({\n      clientId: env.AUTH_WEBAPP_GOOGLE_CLIENT_ID,\n      clientSecret: env.AUTH_WEBAPP_GOOGLE_CLIENT_SECRET,\n    }),\n  ],\n})\n```\n\n```ts filename=\"src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n"
  },
  {
    "path": "docs/pages/guides/extending-the-session.mdx",
    "content": "import { Callout } from \"nextra/components\"\n\n# Extending the Session\n\nAuth.js libraries only expose a subset of the user's information by default in a session to not accidentally expose sensitive user information.\nThis is `name`, `email`, and `image`.\n\n<Callout type=\"info\">\n  All callbacks are async functions, so you can also get extra information from\n  a database or external APIs.\n</Callout>\n\nA common use case is to add the user's id to the session. Below it is shown how to do this based on the session strategy you are using.\n\n## With JWT\n\nTo have access to the user id, add the following to your Auth.js configuration:\n\n```ts filename=\"auth.ts\"\n  //  By default, the `id` property does not exist on `token` or `session`. See the [TypeScript](https://authjs.dev/getting-started/typescript) on how to add it.\n  callbacks: {\n    jwt({ token, user }) {\n      if (user) { // User is available during sign-in\n        token.id = user.id\n      }\n      return token\n    },\n    session({ session, token }) {\n      session.user.id = token.id\n      return session\n    },\n  },\n}\n```\n\nDuring sign-in, the `jwt` callback exposes the user's profile information coming from the provider.\nYou can leverage this to add the user's id to the JWT token. Then, on subsequent calls of this API you will have access to the user's id via `token.id`.\nThen, to expose the user's id in the actual session, you can access `token.id` in the `session` callback and save it on `session.user.id`.\n\nCalls to `auth()` or `useSession()` will now have access to the user's id.\n\n## With Database\n\nIf you are using a database session strategy, you can add the user's id to the session by modifying the `session` callback:\n\n```ts filename=\"auth.ts\"\n  //  By default, the `id` property does not exist on `session`. See the [TypeScript](https://authjs.dev/getting-started/typescript) on how to add it.\n  callbacks: {\n    session({ session, user }) {\n      session.user.id = user.id\n      return session\n    },\n  }\n}\n```\n\nThis will add the user's id to the session object. Notice that in this case, we are getting the id from the `user` object, not the `token`.\nWith the database session strategy, the `user` object is the user from the database, and there is no `token`.\n\nCalls to `auth()` or `useSession()` will now have access to the user's id.\n\n<Callout type=\"warning\">\n  The session object is not persisted server-side, even when using database\n  sessions - only data such as the session token (id), the user, and the expiry\n  time is stored in the session table. If you need to persist session data\n  server-side, you must save it elsewhere. You can connect to the database in\n  the `session()` callback to retrieve this information.\n</Callout>\n\n## With provider functions\n\nWe can extend the default session data in a few ways, one of which is by using the `authorize` and `profile` functions. These functions let us return a user object with the properties we need. We can then create logic based on this information to search in a database or an external API.\n\n```ts\nimport Github from \"next-auth/providers/github\"\nimport Credentials from \"next-auth/providers/credentials\"\nimport type { Provider } from \"next-auth/providers\"\n\nconst providers: Provider[] = [\n  Google({\n    clientId: process.env.AUTH_GOOGLE_ID,\n    clientSecret: process.env.AUTH_GOOGLE_SECRET,\n    async profile(profile) {\n      return { ...profile }\n    },\n  }),\n  Credentials({\n    async authorize(credentials) {\n      return { ...credentials }\n    },\n  }),\n]\n```\n\n## Resources\n\n- [Concepts. Session strategies](/concepts/session-strategies)\n- [TypeScript](/getting-started/typescript)\n"
  },
  {
    "path": "docs/pages/guides/integrating-third-party-backends.mdx",
    "content": "# Integrating with third-party backends\n\nWhen logging in through a provider, you can use the received OAuth tokens to authenticate against a third-party API.\nThese tokens can be used to authorize requests to backends that are supporting the corresponding provider.\n\nFor example:\n\n- GitHub's `access_token` will give you access to GitHub's APIs.\n- Self-managed providers (like [Keycloak](https://www.keycloak.org), [`oidc-provider`](https://github.com/panva/node-oidc-provider), etc.) can be used to authorize against custom third-party backends.\n\n## Storing the token in the session\n\nThe token(s) are made available in the `account` parameter of the jwt callback.\nTo store them in the session, they can be attached to the token first.\n\n```typescript\njwt({ token, trigger, session, account }) {\n  if (account?.provider === \"my-provider\") {\n    return { ...token, accessToken: account.access_token }\n  }\n  // ...\n}\n```\n\nIn order to access the token when making API requests, it needs to be made available to the Auth.js session.\n\n```typescript\nasync session({ session, token }) {\n  session.accessToken = token.accessToken\n  return session\n}\n```\n\n## Using the token to make authorized API requests\n\nOAuth tokens are commonly attached as `Authorization: Bearer <>` header.\nIt is recommended to attach this header server side, like a [Route Handler](https://nextjs.org/docs/app/building-your-application/routing/route-handlers).\n\n```typescript\nexport async function handler(request: NextRequest) {\n  const session = await auth()\n  return await fetch(/*<your-backend-url>/api/authenticated/greeting*/, {\n    headers: { \"Authorization\":  `Bearer ${session?.accessToken}` }\n  })\n  // ...\n}\n```\n\n## Configuring the backend to authorize requests through your provider\n\nConsult your backend framework's documentation on how to verify incoming access tokens.\nBelow is an [example](https://github.com/nextauthjs/authjs-third-party-backend/tree/main/backend-express) with Express.js using a [Keycloak](https://providers.authjs.dev/keycloak) instance.\n\n```javascript\nconst app = express()\nconst jwtCheck = jwt({\n  secret: jwks.expressJwtSecret({\n    cache: true,\n    rateLimit: true,\n    jwksRequestsPerMinute: 5,\n    jwksUri:\n      \"https://keycloak.authjs.dev/realms/master/protocol/openid-connect/certs\",\n  }),\n  issuer: \"https://keycloak.authjs.dev/realms/master\",\n  algorithms: [\"RS256\"],\n})\napp.get(\"*\", jwtCheck, (req, res) => {\n  const name = req.auth?.name ?? \"unknown name\"\n  res.json({ greeting: `Hello, ${name}!` })\n})\n// ...\n```\n\n## Resources\n\n- Further examples for different backend frameworks can be found [here](https://github.com/nextauthjs/authjs-third-party-backend/tree/main).\n- A full example of how to integrate a client app with a third-party API can be found in the [next-auth-example](https://github.com/nextauthjs/next-auth-example).\n- [Keycloak](https://www.keycloak.org) - Open Source Identity and Access Management For Modern Applications and Services\n- [`oidc-provider`](https://github.com/panva/node-oidc-provider) - OpenID Certified™ OAuth 2.0 Authorization Server implementation for Node.js\n"
  },
  {
    "path": "docs/pages/guides/pages/_meta.js",
    "content": "export default {\n  \"built-in-pages\": \"Built-in pages\",\n  signin: \"Custom Signin\",\n  signout: \"Custom Signout\",\n  error: \"Custom Error\",\n}\n"
  },
  {
    "path": "docs/pages/guides/pages/built-in-pages.mdx",
    "content": "import { Code } from \"@/components/Code\"\nimport { Screenshot } from \"@/components/Screenshot\"\n\n# Built-in Pages\n\nAuth.js comes by default with a set of pages that are presented to the user as they go through their authentication journey (sign up, sign in, sign out, error, etc...). This is helpful so that you don't need to write those from scratch when using the library first time. The UI created is based on the providers specified in your configuration file.\n\nIf you do not pass a `providerId`, the `signIn` function will redirect the user to the signin page.\n\nimport SignInPage from \"../../../public/img/tutorials/sign-in-page.webp\"\n\n<Screenshot src={SignInPage} alt=\"Sign-in Page\" />\n\nIn this case the app has been configured with [GitHub](/getting-started/providers/github) and [Credentials](/getting-started/providers/credentials) providers.\n\nIf we added the [Google](/getting-started/providers/google) provider to our Auth.js config file (`./auth.ts`), then a 3rd option to sign in with Google would be available.\n\nIf you'd like to build your own sign in page, checkout our guide on custom sign-in pages.\n"
  },
  {
    "path": "docs/pages/guides/pages/error.mdx",
    "content": "import { Code } from \"@/components/Code\"\nimport { Screenshot } from \"@/components/Screenshot\"\n\n# Custom error page\n\nAuth.js can be configured to display a custom error page when something goes wrong during the user authentication flow (sign in, sign out, etc..).\n\nIn order to override Auth.js's `/api/auth/error` page we have to define our custom page in the AuthConfig:\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nconst authConfig: NextAuthConfig = {\n...\n  pages: {\n    error: \"/error\",\n  }\n...\n};\n```\n\n</Code.Next>\n</Code>\n\nUsing the [example app](https://github.com/nextauthjs/next-auth-example), let's build a simple custom error page by creating `app/error/page.tsx`\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"app/error/page.tsx\"\nexport default function AuthErrorPage() {\n  return <>Oops</>\n}\n```\n\n</Code.Next>\n</Code>\n\nAuth.js forwards the following errors as error query parameters in the URL to our custom error page:\n\n| Query Param     | Example URL                       | Description                                                                                   |\n| --------------- | --------------------------------- | --------------------------------------------------------------------------------------------- |\n| `Configuration` | `/auth/error?error=Configuration` | There is a problem with the server configuration. Check if your options are correct.          |\n| `AccessDenied`  | `/auth/error?error=AccessDenied`  | Usually occurs, when you restricted access through the signIn callback, or redirect callback. |\n| `Verification`  | `/auth/error?error=Verification`  | Related to the Email provider. The token has expired or has already been used.                |\n| `Default`       | `/auth/error?error=Default`       | Catch all, will apply, if none of the above matched.                                          |\n\nSo now we can update our custom error page with it:\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"app/error/page.tsx\"\n\"use client\"\n\nimport { useSearchParams } from \"next/navigation\"\n\nenum Error {\n  Configuration = \"Configuration\",\n}\n\nconst errorMap = {\n  [Error.Configuration]: (\n    <p>\n      There was a problem when trying to authenticate. Please contact us if this\n      error persists. Unique error code:{\" \"}\n      <code className=\"rounded-sm bg-slate-100 p-1 text-xs\">Configuration</code>\n    </p>\n  ),\n}\n\nexport default function AuthErrorPage() {\n  const search = useSearchParams()\n  const error = search.get(\"error\") as Error\n\n  return (\n    <div className=\"flex h-screen w-full flex-col items-center justify-center\">\n      <a\n        href=\"#\"\n        className=\"block max-w-sm rounded-lg border border-gray-200 bg-white p-6 text-center shadow hover:bg-gray-100 dark:border-gray-700 dark:bg-gray-800 dark:hover:bg-gray-700\"\n      >\n        <h5 className=\"mb-2 flex flex-row items-center justify-center gap-2 text-xl font-bold tracking-tight text-gray-900 dark:text-white\">\n          Something went wrong\n        </h5>\n        <div className=\"font-normal text-gray-700 dark:text-gray-400\">\n          {errorMap[error] || \"Please contact us if this error persists.\"}\n        </div>\n      </a>\n    </div>\n  )\n}\n```\n\n</Code.Next>\n</Code>\n\nNow, when an error happens, Auth.js will redirect the user to our custom error page:\n\nimport CustomErrorPage from \"../../../public/img/tutorials/custom-error-page.webp\"\n\n<Screenshot src={CustomErrorPage} alt=\"Custom Error Page\" />\n"
  },
  {
    "path": "docs/pages/guides/pages/signin.mdx",
    "content": "import { Code } from \"@/components/Code\"\nimport { Screenshot } from \"@/components/Screenshot\"\n\n# Custom Sign-in Page\n\nTo add a custom sign-in page, you'll need to define the path to your page in the [`pages`](/reference/core/types#pagesoptions) object in your Auth.js configuration. Make sure a route / page actually exists at the path you're defining here!\n\nAdditionally, we'll have to export a map of `provider.id` and `provider.name` to easily consume in our custom page if we want to dynamically render the correct buttons, based on what we've defined in our `auth.ts` configuration. Because you can pass your providers to the `providers` array as both a function, or the result of calling that function, this example `providerMap` handles both cases.\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"./auth.ts\" {6, 21-28, 32-34}\nimport NextAuth from \"next-auth\"\nimport GitHub from \"next-auth/providers/github\"\nimport Credentials from \"next-auth/providers/credentials\"\nimport type { Provider } from \"next-auth/providers\"\n\nconst providers: Provider[] = [\n  Credentials({\n    credentials: { password: { label: \"Password\", type: \"password\" } },\n    authorize(c) {\n      if (c.password !== \"password\") return null\n      return {\n        id: \"test\",\n        name: \"Test User\",\n        email: \"test@example.com\",\n      }\n    },\n  }),\n  GitHub,\n]\n\nexport const providerMap = providers\n  .map((provider) => {\n    if (typeof provider === \"function\") {\n      const providerData = provider()\n      return { id: providerData.id, name: providerData.name }\n    } else {\n      return { id: provider.id, name: provider.name }\n    }\n  })\n  .filter((provider) => provider.id !== \"credentials\")\n\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers,\n  pages: {\n    signIn: \"/signin\",\n  },\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Credentials from \"@auth/qwik/providers/credentials\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Credentials({\n        credentials: {\n          username: { label: \"Username\" },\n          password: { label: \"Password\", type: \"password\" },\n        },\n        async authorize({ request }) {\n          if (c.password !== \"password\") return null\n          return {\n            id: \"test\",\n            name: \"Test User\",\n            email: \"test@example.com\",\n          }\n        },\n      }),\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```tsx filename=\"./src/auth.ts\" {6, 21-28, 32-34}\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport GitHub from \"@auth/sveltekit/providers/github\"\nimport Credentials from \"@auth/sveltekit/providers/credentials\"\nimport type { Provider } from \"@auth/sveltekit/providers\"\n\nconst providers: Provider[] = [\n  GitHub,\n  Credentials({\n    credentials: { password: { label: \"Password\", type: \"password\" } },\n    authorize(c) {\n      if (c.password !== \"password\") return null\n      return {\n        id: \"test\",\n        name: \"Test User\",\n        email: \"test@example.com\",\n      }\n    },\n  }),\n]\n\nexport const providerMap = providers.map((provider) => {\n  if (typeof provider === \"function\") {\n    const providerData = provider()\n    return { id: providerData.id, name: providerData.name }\n  } else {\n    return { id: provider.id, name: provider.name }\n  }\n})\n\nexport const { handle, signIn, signOut } = SvelteKitAuth({\n  providers,\n  pages: {\n    signIn: \"/signin\",\n  },\n})\n```\n\n</Code.Svelte>\n</Code>\n\nWe can now build our own custom sign in page.\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"app/signin/page.tsx\" /providerMap/\nimport { redirect } from \"next/navigation\"\nimport { signIn, auth, providerMap } from \"@/auth.ts\"\nimport { AuthError } from \"next-auth\"\n\nconst SIGNIN_ERROR_URL = \"/error\"\n\nexport default async function SignInPage(props: {\n  searchParams: { callbackUrl: string | undefined }\n}) {\n  return (\n    <div className=\"flex flex-col gap-2\">\n      <form\n        action={async (formData) => {\n          \"use server\"\n          try {\n            await signIn(\"credentials\", formData)\n          } catch (error) {\n            if (error instanceof AuthError) {\n              return redirect(`${SIGNIN_ERROR_URL}?error=${error.type}`)\n            }\n            throw error\n          }\n        }}\n      >\n        <label htmlFor=\"email\">\n          Email\n          <input name=\"email\" id=\"email\" />\n        </label>\n        <label htmlFor=\"password\">\n          Password\n          <input name=\"password\" id=\"password\" />\n        </label>\n        <input type=\"submit\" value=\"Sign In\" />\n      </form>\n      {Object.values(providerMap).map((provider) => (\n        <form\n          key={provider.id}\n          action={async () => {\n            \"use server\"\n            try {\n              await signIn(provider.id, {\n                redirectTo: props.searchParams?.callbackUrl ?? \"\",\n              })\n            } catch (error) {\n              // Signin can fail for a number of reasons, such as the user\n              // not existing, or the user not having the correct role.\n              // In some cases, you may want to redirect to a custom error\n              if (error instanceof AuthError) {\n                return redirect(`${SIGNIN_ERROR_URL}?error=${error.type}`)\n              }\n\n              // Otherwise if a redirects happens Next.js can handle it\n              // so you can just re-thrown the error and let Next.js handle it.\n              // Docs:\n              // https://nextjs.org/docs/app/api-reference/functions/redirect#server-component\n              throw error\n            }\n          }}\n        >\n          <button type=\"submit\">\n            <span>Sign in with {provider.name}</span>\n          </button>\n        </form>\n      ))}\n    </div>\n  )\n}\n```\n\n</Code.Next>\n<Code.Qwik>\n\nWith Qwik we can do a server-side sign in with Form action, or a more\nsimple client-side sign in via submit method.\n\n```ts filename=\"./components/sign-in.tsx\"\nimport { component$ } from \"@builder.io/qwik\"\nimport { Form, Link } from \"@builder.io/qwik-city\"\nimport { useSignIn } from \"./plugin@auth\"\n\nexport default component$(() => {\n  const signInSig = useSignIn()\n\n  return (\n    <>\n      {/* server-side login with Form action */}\n      <Form action={signInSig}>\n        <input type=\"hidden\" name=\"providerId\" value=\"<providerId>\" />\n        <input\n          type=\"hidden\"\n          name=\"options.redirectTo\"\n          value=\"/\"\n        />\n        <button>Sign In</button>\n      </Form>\n\n      {/* submit method */}\n      <Link\n        onClick$={() => signInSig.submit({ redirectTo: \"/\" })}\n      >\n        SignIn\n      </Link>\n    </>\n  )\n})\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```svelte filename=\"src/routes/signin/+page.svelte\" /submitButton/ /providerMap/\n<script lang=\"ts\">\n  import { SignIn } from \"@auth/sveltekit/components\"\n  import { providerMap } from \"../../auth.ts\"\n</script>\n\n<div>\n  <div>\n    <h2>\n      <img src=\"/img/logo.png\" alt=\"Company Logo\" />\n      <span>Company</span>\n    </h2>\n    <div>\n      {#each providerMap as provider}\n        <SignIn provider={provider.id} signInPage=\"signin\" className=\"w-full\">\n          <div slot=\"submitButton\">\n            <span>\n              Signin with {provider.name}\n            </span>\n          </div>\n        </SignIn>\n      {/each}\n    </div>\n  </div>\n</div>\n```\n\nYou'll also need to create this server action in SvelteKit to handle the action at `/signin`. That's the default path, but this can be changed with the `signInPage` prop on the `SignIn` component above.\n\n```ts filename=\"src/routes/signin/+page.server.ts\"\nimport { signIn } from \"../../auth\"\nimport type { Actions } from \"./$types\"\n\nexport const actions = { default: signIn } satisfies Actions\n```\n\n</Code.Svelte>\n</Code>\n\nThen when calling `signIn` without any arguments anywhere in your application, the custom signin page will appear.\n\nimport CustomSignInPage from \"../../../public/img/tutorials/custom-sign-in-page.webp\"\n\n<Screenshot\n  src={CustomSignInPage}\n  alt=\"Custom Sign-in Page\"\n  className=\"w-full max-w-3xl\"\n/>\n"
  },
  {
    "path": "docs/pages/guides/pages/signout.mdx",
    "content": "import { Code } from \"@/components/Code\"\nimport { Screenshot } from \"@/components/Screenshot\"\n\n# Custom sign out page\n\nIs easy to configure Auth.js to display a custom sign out page in case you need it.\n\nHere's the code for a simple sign out page built on top of the [example app](https://github.com/nextauthjs/next-auth-example):\n\n<Code>\n<Code.Next>\n\n```tsx filename=\"app/signout/page.tsx\" {10}\nimport { signOut } from \"@/auth\"\n\nexport default function SignOutPage() {\n  return (\n    <div>\n      <h5>Are you sure you want to sign out?</h5>\n      <form\n        action={async (formData) => {\n          \"use server\"\n          await signOut()\n        }}\n      >\n        <button type=\"submit\">Sign out</button>\n      </form>\n    </div>\n  )\n}\n```\n\n</Code.Next>\n<Code.Qwik>\n\nWith Qwik we can do a server-side sign out with Form action, or a more\nsimple client-side sign out via submit method.\n\n```ts filename=\"./components/sign-out.tsx\"\nimport { component$ } from \"@builder.io/qwik\"\nimport { Form, Link } from \"@builder.io/qwik-city\"\nimport { useSignOut } from \"./plugin@auth\"\n\nexport default component$(() => {\n  const signOutSig = useSignOut()\n\n  return (\n    <>\n      {/* server-side with Form action */}\n      <Form action={signOutSig}>\n        <input type=\"hidden\" name=\"redirectTo\" value=\"/signedout\" />\n        <button>Sign Out</button>\n      </Form>\n\n      {/* submit method */}\n      <Link onClick$={() => signOutSig.submit({ redirectTo: \"/\" })}>\n        SignIn\n      </Link>\n    </>\n  )\n})\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```svelte filename=\"src/routes/signout/+page.svelte\" {13} /submitButton/\n<script lang=\"ts\">\n  import { SignOut } from \"@auth/sveltekit/components\"\n</script>\n\n<div class=\"relative flex h-full w-full overflow-hidden\">\n  <div\n    class=\"z-20 flex h-dvh w-full items-center justify-center md:ml-[15%] md:w-[22rem]\"\n  >\n    <div class=\"flex w-80 flex-col items-center justify-center text-xl\">\n      <h2\n        class=\"mb-4 flex items-center space-x-2 text-3xl font-light text-slate-600\"\n      >\n        <img src=\"/img/logo.png\" alt=\"Company Logo\" class=\"size-5\" />\n        <span class=\"text-4xl font-medium text-white\">Company</span>\n      </h2>\n      <div\n        class=\"m-8 flex w-full flex-col gap-2 rounded bg-white p-6 shadow-lg\"\n      >\n        <SignOut signOutPage=\"signout\" className=\"w-full\">\n          <div\n            slot=\"submitButton\"\n            class=\"mt-2 flex h-12 w-full items-center space-x-2 rounded px-4 text-base font-light transition focus:outline-none focus:ring-2 focus:ring-slate-800 focus:ring-offset-2\"\n          >\n            <span class=\"flex w-full justify-center\"> Signout </span>\n          </div>\n        </SignOut>\n      </div>\n    </div>\n  </div>\n</div>\n```\n\nYou'll also need to create this server page in SvelteKit to handle the action at `/signout`. That's the default path, but this can be changed with the `signOutPage` prop on the `SignOut` component above.\n\n```ts filename=\"src/routes/signout/+page.server.ts\"\nimport { signOut } from \"../../auth\"\nimport type { Actions } from \"./$types\"\n\nexport const actions = { default: signOut } satisfies Actions\n```\n\n</Code.Svelte>\n</Code>\n\nNow if the user navigates to `/signout` they'll see the following page:\n\nimport CustomSignOut from \"../../../public/img/tutorials/custom-sign-out.webp\"\n\n<Screenshot src={CustomSignOut} alt=\"Custom Sign-out\" />\n\nIf they click \"Sign out\", the session will be deleted and they will be redirected to the homepage.\n"
  },
  {
    "path": "docs/pages/guides/refresh-token-rotation.mdx",
    "content": "import { Code } from \"@/components/Code\"\nimport { Callout } from \"nextra/components\"\n\n<Callout>\n  As of today, there is no built-in solution for automatic Refresh Token\n  rotation. This guide will help you to achieve this in your application. Our\n  goal is to add zero-config support for built-in providers eventually. [Let us\n  know](/contributors#core-team) if you would like to help.\n</Callout>\n\n## What is refresh token rotation?\n\nRefresh token rotation is the practice of updating an `access_token` on behalf of the user, without requiring interaction (ie.: re-authenticating).\n`access_token`s are usually issued for a limited time. After they expire, the service verifying them will ignore the value, rendering the `access_token` useless.\nInstead of asking the user to sign in again to obtain a new `access_token`, many providers also issue a `refresh_token` during initial signin, that has a longer expiry date.\nAuth.js libraries can be configured to use this `refresh_token` to obtain a new `access_token` without requiring the user to sign in again.\n\n## Implementation\n\n<Callout type=\"info\">\n  There is an inherent limitation of the following guides that comes from the\n  fact, that - for security reasons - `refresh_token`s are usually only usable\n  once. Meaning that after a successful refresh, the `refresh_token` will be\n  invalidated and cannot be used again. Therefore, in some cases, a\n  race-condition might occur if multiple requests will try to refresh the token\n  at the same time. The Auth.js team is aware of this and would like to provide\n  a solution in the future. This might include some \"lock\" mechanism to prevent\n  multiple requests from trying to refresh the token at the same time, but that\n  comes with the drawback of potentially creating a bottleneck in the\n  application. Another possible solution is background token refresh, to prevent\n  the token from expiring during an authenticated request.\n</Callout>\n\nFirst, make sure that the provider you want to use supports `refresh_token`'s. Check out [The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749#section-6) spec for more details.\nDepending on the [session strategy](/concepts/session-strategies), the `refresh_token` can be persisted either in an encrypted JWT inside a cookie or in a database.\n\n### JWT strategy\n\n<Callout>\n  While using a cookie to store the `refresh_token` is simpler, it is less\n  secure. To mitigate risks with the `strategy: \"jwt\"`, Auth.js libraries store\n  the `refresh_token` in an _encrypted_ JWT, in an `HttpOnly` cookie. Still, you\n  need to evaluate based on your requirements which strategy you choose.\n</Callout>\n\nUsing the [jwt](/reference/core/types#jwt) and [session](/reference/core/types#session) callbacks, we can persist OAuth tokens and refresh them when they expire.\n\nBelow is a sample implementation of refreshing the `access_token` with Google. Please note that the OAuth 2.0 request to get the `refresh_token` will vary between different providers, but the rest of logic should remain similar.\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth, { type User } from \"next-auth\"\nimport Google from \"next-auth/providers/google\"\n\nexport const { handlers, auth } = NextAuth({\n  providers: [\n    Google({\n      // Google requires \"offline\" access_type to provide a `refresh_token`\n      authorization: { params: { access_type: \"offline\", prompt: \"consent\" } },\n    }),\n  ],\n  callbacks: {\n    async jwt({ token, account }) {\n      if (account) {\n        // First-time login, save the `access_token`, its expiry and the `refresh_token`\n        return {\n          ...token,\n          access_token: account.access_token,\n          expires_at: account.expires_at,\n          refresh_token: account.refresh_token,\n        }\n      } else if (Date.now() < token.expires_at * 1000) {\n        // Subsequent logins, but the `access_token` is still valid\n        return token\n      } else {\n        // Subsequent logins, but the `access_token` has expired, try to refresh it\n        if (!token.refresh_token) throw new TypeError(\"Missing refresh_token\")\n\n        try {\n          // The `token_endpoint` can be found in the provider's documentation. Or if they support OIDC,\n          // at their `/.well-known/openid-configuration` endpoint.\n          // i.e. https://accounts.google.com/.well-known/openid-configuration\n          const response = await fetch(\"https://oauth2.googleapis.com/token\", {\n            method: \"POST\",\n            body: new URLSearchParams({\n              client_id: process.env.AUTH_GOOGLE_ID!,\n              client_secret: process.env.AUTH_GOOGLE_SECRET!,\n              grant_type: \"refresh_token\",\n              refresh_token: token.refresh_token!,\n            }),\n          })\n\n          const tokensOrError = await response.json()\n\n          if (!response.ok) throw tokensOrError\n\n          const newTokens = tokensOrError as {\n            access_token: string\n            expires_in: number\n            refresh_token?: string\n          }\n\n          return {\n            ...token,\n            access_token: newTokens.access_token,\n            expires_at: Math.floor(Date.now() / 1000 + newTokens.expires_in),\n            // Some providers only issue refresh tokens once, so preserve if we did not get a new one\n            refresh_token: newTokens.refresh_token\n              ? newTokens.refresh_token\n              : token.refresh_token,\n          }\n        } catch (error) {\n          console.error(\"Error refreshing access_token\", error)\n          // If we fail to refresh the token, return an error so we can handle it on the page\n          token.error = \"RefreshTokenError\"\n          return token\n        }\n      }\n    },\n    async session({ session, token }) {\n      session.error = token.error\n      return session\n    },\n  },\n})\n\ndeclare module \"next-auth\" {\n  interface Session {\n    error?: \"RefreshTokenError\"\n  }\n}\n\ndeclare module \"next-auth/jwt\" {\n  interface JWT {\n    access_token: string\n    expires_at: number\n    refresh_token?: string\n    error?: \"RefreshTokenError\"\n  }\n}\n```\n\n  </Code.Next>\n</Code>\n\n### Database strategy\n\nUsing the database session strategy is similar, but instead we will save the `access_token`, `expires_at` and `refresh_token` on the `account` for the given provider.\n\n<Code>\n  <Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Google from \"next-auth/providers/google\"\nimport { PrismaAdapter } from \"@auth/prisma-adapter\"\nimport { PrismaClient } from \"@prisma/client\"\n\nconst prisma = new PrismaClient()\n\nexport const { handlers, signIn, signOut, auth } = NextAuth({\n  adapter: PrismaAdapter(prisma),\n  providers: [\n    Google({\n      authorization: { params: { access_type: \"offline\", prompt: \"consent\" } },\n    }),\n  ],\n  callbacks: {\n    async session({ session, user }) {\n      const [googleAccount] = await prisma.account.findMany({\n        where: { userId: user.id, provider: \"google\" },\n      })\n      if (googleAccount.expires_at * 1000 < Date.now()) {\n        // If the access token has expired, try to refresh it\n        try {\n          // https://accounts.google.com/.well-known/openid-configuration\n          // We need the `token_endpoint`.\n          const response = await fetch(\"https://oauth2.googleapis.com/token\", {\n            method: \"POST\",\n            body: new URLSearchParams({\n              client_id: process.env.AUTH_GOOGLE_ID!,\n              client_secret: process.env.AUTH_GOOGLE_SECRET!,\n              grant_type: \"refresh_token\",\n              refresh_token: googleAccount.refresh_token,\n            }),\n          })\n\n          const tokensOrError = await response.json()\n\n          if (!response.ok) throw tokensOrError\n\n          const newTokens = tokensOrError as {\n            access_token: string\n            expires_in: number\n            refresh_token?: string\n          }\n\n          await prisma.account.update({\n            data: {\n              access_token: newTokens.access_token,\n              expires_at: Math.floor(Date.now() / 1000 + newTokens.expires_in),\n              refresh_token:\n                newTokens.refresh_token ?? googleAccount.refresh_token,\n            },\n            where: {\n              provider_providerAccountId: {\n                provider: \"google\",\n                providerAccountId: googleAccount.providerAccountId,\n              },\n            },\n          })\n        } catch (error) {\n          console.error(\"Error refreshing access_token\", error)\n          // If we fail to refresh the token, return an error so we can handle it on the page\n          session.error = \"RefreshTokenError\"\n        }\n      }\n      return session\n    },\n  },\n})\n\ndeclare module \"next-auth\" {\n  interface Session {\n    error?: \"RefreshTokenError\"\n  }\n}\n```\n\n  </Code.Next>\n</Code>\n\n### Error handling\n\nIf the token refresh was unsuccessful, we can force a re-authentication.\n\n<Code>\n\n<Code.Next>\n\n```tsx filename=\"app/dashboard/page.tsx\"\nimport { auth, signIn } from \"@/auth\"\n\nexport default async function Page() {\n  const session = await auth()\n  if (session?.error === \"RefreshTokenError\") {\n    await signIn(\"google\") // Force sign in to obtain a new set of access and refresh tokens\n  }\n}\n```\n\n</Code.Next>\n\n<Code.NextClient>\n\n```tsx filename=\"app/dashboard/page.tsx\"\n\"use client\"\n\nimport { useEffect } from \"react\"\nimport { signIn, useSession } from \"next-auth/react\"\n\nexport default function Page() {\n  const { data: session } = useSession() // For this to work, the Page should be wrapped inside the SessionProvider component in Layout\n  useEffect(() => {\n    if (session?.error !== \"RefreshTokenError\") return\n    signIn(\"google\") // Force sign in to obtain a new set of access and refresh tokens\n  }, [session?.error])\n}\n```\n\n</Code.NextClient>\n\n</Code>\n"
  },
  {
    "path": "docs/pages/guides/restricting-user-access.mdx",
    "content": "import { Callout } from \"nextra/components\"\n\n# Restricting user access to the app\n\nAuth.js libraries allow you to restrict users by intercepting the registration/login flow. You can use the `signIn` callback to control whether a user is allowed to sign up or not.\n\n<Callout type=\"info\">\n  All callbacks are async functions, so you can also get extra information from\n  a database or external APIs.\n</Callout>\n\n## Restrict the app to company employees\n\nFor example, it is possible to only allow your company's employees to sign up with their company email addresses.\n\nAdd the following code to your Auth.js configuration:\n\n```ts\ncallbacks: {\n  signIn({ profile }) {\n    return profile.email.endsWith(\"@yourdomain.com\")\n  }\n}\n```\n\nIf the user's email does not end with `@yourdomain.com`,\nthe sign-up (in case of a database strategy) process will be blocked, and subsequent login attempts will be rejected as well.\n"
  },
  {
    "path": "docs/pages/guides/role-based-access-control.mdx",
    "content": "import { Callout } from \"nextra/components\"\nimport { Code } from \"@/components/Code\"\n\n# Role-based access control\n\nThere are two ways to add [role-based access control (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control) to your application with Auth.js, based on the [session strategy](/concepts/session-strategies) you choose. Let's see an example for each of these.\n\n## Getting the role\n\nStart by adding a `profile()` callback to the providers' config to determine the user role:\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Google from \"next-auth/providers/google\"\n\nexport const { handlers, auth } = NextAuth({\n  providers: [\n    Google({\n      profile(profile) {\n        return { role: profile.role ?? \"user\", ...profile }\n      },\n    }),\n  ],\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Google from \"@auth/qwik/providers/google\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Google({\n        profile(profile) {\n          return { role: profile.role ?? \"user\", ...profile }\n        },\n      })\n    ],\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport Google from \"@auth/sveltekit/providers/google\"\n\nexport const { handle } = SvelteKitAuth({\n  providers: [\n    Google({\n      profile(profile) {\n        return { role: profile.role ?? \"user\", ...profile }\n      },\n    }),\n  ],\n})\n```\n\n</Code.Svelte>\n</Code>\n\n<Callout>\n  Determining the users role is your responsibility, you can either add your own\n  logic or if your provider returns a role you can use that instead.\n</Callout>\n\n## Persisting the role\n\nPersisting the role will be different depending on the [session strategy](/concepts/session-strategies) you're using. If you don't know which session strategy you're using, then most likely you're using JWT (the default one).\n\n### With JWT\n\nWhen you don't have a database configured, the role will be persisted in a cookie, by using the `jwt()` callback. On sign-in, the `role` property is exposed from the `profile` callback on the `user` object. Persist the `user.role` value by assigning it to `token.role`. That's it!\n\nIf you also want to use the role on the client, you can expose it via the `session` callback.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\" {8}\nimport NextAuth from \"next-auth\"\nimport Google from \"next-auth/providers/google\"\n\nexport const { handlers, auth } = NextAuth({\n  providers: [\n    Google({\n      profile(profile) {\n        return { role: profile.role ?? \"user\", ...profile }\n      },\n    }),\n  ],\n  callbacks: {\n    jwt({ token, user }) {\n      if (user) token.role = user.role\n      return token\n    },\n    session({ session, token }) {\n      session.user.role = token.role\n      return session\n    },\n  },\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n  \n```ts filename=\"/src/routes/plugin@auth.ts\" {9}\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport Google from \"@auth/qwik/providers/google\"\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [\n      Google({\n        profile(profile) {\n          return { role: profile.role ?? \"user\", ...profile }\n        },\n      })\n    ],\n    callbacks: {\n      jwt({ token, user }) {\n        if(user) token.role = user.role\n        return token\n      },\n      session({ session, token }) {\n        session.user.role = token.role\n        return session\n      }\n    }\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\" {8}\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport Google from \"@auth/sveltekit/providers/google\"\n\nexport const { handle } = SvelteKitAuth({\n  providers: [\n    Google({\n      profile(profile) {\n        return { role: profile.role ?? \"user\", ...profile }\n      },\n    }),\n  ],\n  callbacks: {\n    jwt({ token, user }) {\n      if (user) token.role = user.role\n      return token\n    },\n    session({ session, token }) {\n      session.user.role = token.role\n      return session\n    },\n  },\n})\n```\n\n</Code.Svelte>\n</Code>\n\n<Callout type=\"info\">\n  With this strategy, if you want to update the role, the user needs to be\n  forced to sign in again.\n</Callout>\n\n### With Database\n\nWhen you have a database, you can save the user role on the [User model](/reference/core/adapters#adapteruser). The below example is showing you how to do this with Prisma, but the idea is the same for all adapters.\n\nFirst, add a `role` column to the User model.\n\n```prisma filename=\"/prisma/schema.prisma\"\nmodel User {\n  id            String    @id @default(cuid())\n  name          String?\n  email         String?   @unique\n  emailVerified DateTime?\n  image         String?\n  role          String?  // New column\n  accounts      Account[]\n  sessions      Session[]\n}\n```\n\nThe `profile()` callback's return value is used to create users in the database. That's it! Your newly created users will now have an assigned role.\n\nIf you also want to use the role on the client, you can expose it via the `session` callback.\n\n<Code>\n<Code.Next>\n\n```ts filename=\"./auth.ts\"\nimport NextAuth from \"next-auth\"\nimport Google from \"next-auth/providers/google\"\nimport prisma from \"lib/prisma\"\n\nexport const { handlers, auth } = NextAuth({\n  adapter: PrismaAdapter(prisma),\n  providers: [\n    Google({\n      profile(profile) {\n        return { role: profile.role ?? \"user\", ...profile }\n      },\n    }),\n  ],\n  callbacks: {\n    session({ session, user }) {\n      session.user.role = user.role\n      return session\n    },\n  },\n})\n```\n\n</Code.Next>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nimport { QwikAuth$ } from \"@auth/qwik\"\nimport { PrismaAdapter } from \"@auth/prisma-adapter\"\nimport { PrismaClient } from \"@prisma/client\"\n\nconst prisma = new PrismaClient()\n\nexport const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(\n  () => ({\n    providers: [],\n    adapter: PrismaAdapter(prisma),\n    callbacks: {\n      session({ session, user }) {\n        session.user.role = user.role\n        return session\n      },\n    },\n  })\n)\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./src/auth.ts\"\nimport SvelteKitAuth from \"@auth/sveltekit\"\nimport Google from \"@auth/sveltekit/providers/google\"\nimport prisma from \"lib/prisma\"\n\nexport const { handle, auth } = SvelteKitAuth({\n  adapter: PrismaAdapter(prisma),\n  providers: [\n    Google({\n      profile(profile) {\n        return { role: profile.role ?? \"user\", ...profile }\n      },\n    }),\n  ],\n  callbacks: {\n    session({ session, user }) {\n      session.user.role = user.role\n      return session\n    },\n  },\n})\n```\n\n```ts filename=\"src/hooks.server.ts\"\nexport { handle } from \"./auth\"\n```\n\n</Code.Svelte>\n</Code>\n\n<Callout type=\"info\">\n  It is up to you how you want to manage to update the roles, either through\n  direct database access or building your role update API.\n</Callout>\n\n## Using the role\n\nThe user can access the information stored in the current session through the authorization function exported from the configuration file of the respective framework. This function retrieves information exposed via the `session` and `jwt` callbacks in the configuration file. With this information, you can implement different strategies and logic to display the UI based on your needs.\n\n<Code>\n<Code.Next>\nTo get the data on the server side, you should import the `auth` function from the configuration file and verify if the user has the expected role.\n\n```ts filename=\"./app/admin/page.tsx\"\nimport { auth } from \"@/auth\";\n\nexport default async function Page() {\n  const session = await auth();\n\n  if (session?.user?.role === \"admin\") {\n    return <p>You are an admin, welcome!</p>;\n  }\n\n  return <p>You are not authorized to view this page!</p>;\n}\n```\n\n</Code.Next>\n<Code.NextClient>\nIf you want to use the role on the client side, use the `useSession` hook. The `session.user.role` will contain the required role if you exposed it via the `session` callback.\n\n```ts filename=\"./app/admin/page.tsx\"\n\"use client\"\nimport { useSession } from \"next-auth/react\";\n\nexport default function Page() {\n  const session = useSession();\n\n  if (session?.user?.role === \"admin\") {\n    return <p>You are an admin, welcome!</p>;\n  }\n\n  return <p>You are not authorized to view this page!</p>;\n}\n```\n\n</Code.NextClient>\n<Code.Qwik>\n\n```ts filename=\"/src/routes/plugin@auth.ts\"\nexport const onRequest: RequestHandler = (event) => {\n  const session = event.sharedMap.get(\"session\")\n  if (!session || new Date(session.expires) < new Date()) {\n    throw event.redirect(302, `/auth/signin?redirectTo=${event.url.pathname}`)\n  }\n\n  return session\n}\n```\n\n</Code.Qwik>\n<Code.Svelte>\n\n```ts filename=\"./routes/+page.server.ts\"\nimport { redirect } from \"@sveltejs/kit\"\n\nexport const load: PageServerLoad = async (event) => {\n  const session = await event.locals.auth()\n\n  if (!session && event.url.pathname !== \"/login\") {\n    const fromUrl = event.url.pathname + event.url.search\n    redirect(307, `/login?redirectTo=${encodeURIComponent(fromUrl)}`)\n  }\n\n  return {\n    session,\n  }\n}\n```\n\n</Code.Svelte>\n</Code>\n\n<Callout>\n  When using Next.js and JWT, you can alternatively also use\n  [Proxy](/getting-started/session-management/protecting#nextjs-proxy) to\n  redirect the user based on their role, even before rendering the page.\n</Callout>\n\n## Resources\n\n- [Concepts: Session strategies](/concepts/session-strategies)\n- [Adapters: User model](/getting-started/database#user-model)\n- [Adapters: Prisma adapter](/getting-started/adapters/prisma)\n- [Next.js: Proxy](/getting-started/session-management/protecting#nextjs-proxy)\n- [TypeScript](/getting-started/typescript)\n"
  },
  {
    "path": "docs/pages/guides/testing.mdx",
    "content": "# Testing\n\nRepeated and consistent testing of authentication has always been tricky. OAuth providers in particular are especially difficult to test in an automated fashion, because they often introduce additional verification steps that will trigger if you're logging in from a new geographic location, a datacenter IP address, or from a new user-agent, etc.\n\nTo get around these limitations, we recommend you use one of the following strategies to run successful E2E tests against Auth.js applications.\n\n1. Run your own OAuth provider using software like [Keycloak](https://www.keycloak.org)\n2. Enable an authentication method like the Credentials provider in development mode\n\nBelow are one example of each strategy, leveraging [@playwright/test](https://playwright.dev) for the automated E2E tests.\n\n## Keycloak\n\nFirst, set up a [Keycloak](https://www.keycloak.org/getting-started/getting-started-docker) instance. Then you have to add the [Keycloak provider](/getting-started/providers/keycloak) to your Auth.js configuration.\n\nThis test requires two environment variables to be set. These credentials should be for a test user who can authenticate against your newly created Keycloak instance.\n\n```bash filename=\".env\"\nTEST_KEYCLOAK_USERNAME=abc\nTEST_KEYCLOAK_PASSWORD=123\n```\n\nThen we can use [`@playwright/test`](https://playwright.dev) to execute two test steps.\n\n1. Which will visit the signin URL, enter the authentication credentials, and then click the \"Sign In\" button. It ensures the session is then set correctly.\n2. Which will click the \"Sign Out\" button and ensure the session is then `null`.\n\n```ts filename=\"tests/e2e/basic-auth.spec.ts\" {10, 32}\nimport { test, expect, type Page } from \"@playwright/test\"\n\ntest(\"Basic auth\", async ({ page, browser }) => {\n  if (\n    !process.env.TEST_KEYCLOAK_USERNAME ||\n    !process.env.TEST_KEYCLOAK_PASSWORD\n  )\n    throw new TypeError(\"Incorrect TEST_KEYCLOAK_{USERNAME,PASSWORD}\")\n\n  await test.step(\"should login\", async () => {\n    await page.goto(\"http://localhost:3000/auth/signin\")\n    await page.getByText(\"Keycloak\").click()\n    await page.getByText(\"Username or email\").waitFor()\n    await page\n      .getByLabel(\"Username or email\")\n      .fill(process.env.TEST_KEYCLOAK_USERNAME)\n    await page.locator(\"#password\").fill(process.env.TEST_KEYCLOAK_PASSWORD)\n    await page.getByRole(\"button\", { name: \"Sign In\" }).click()\n    await page.waitForURL(\"http://localhost:3000\")\n    const session = await page.locator(\"pre\").textContent()\n\n    expect(JSON.parse(session ?? \"{}\")).toEqual({\n      user: {\n        email: \"bob@alice.com\",\n        name: \"Bob Alice\",\n        image: \"https://avatars.githubusercontent.com/u/67470890?s=200&v=4\",\n      },\n      expires: expect.any(String),\n    })\n  })\n\n  await test.step(\"should logout\", async () => {\n    await page.getByText(\"Sign out\").click()\n    await page\n      .locator(\"header\")\n      .getByRole(\"button\", { name: \"Sign in\", exact: true })\n      .waitFor()\n    await page.goto(\"http://localhost:3000/auth/session\")\n\n    expect(await page.locator(\"html\").textContent()).toBe(\"null\")\n  })\n})\n```\n\n## `Credentials` Provider in Development\n\nThis method requires less initial setup and maintenance as you do not need to maintain a separate OAuth provider (like Keycloak), but you also must be extremely careful that you do not leave insecure authentication methods available in production. For example, in this example we will be adding a Credentials provider which accepts the password `password`.\n\n```ts filename=\"auth.ts\" {16}\nimport NextAuth from \"next-auth\"\nimport GitHub from \"next-auth/providers/github\"\nimport Credentials from \"next-auth/providers/credentials\"\n\nconst providers = [GitHub]\n\nif (process.env.NODE_ENV === \"development\") {\n  providers.push(\n    Credentials({\n      id: \"password\",\n      name: \"Password\",\n      credentials: {\n        password: { label: \"Password\", type: \"password\" },\n      },\n      authorize: (credentials) => {\n        if (credentials.password === \"password\") {\n          return {\n            email: \"bob@alice.com\",\n            name: \"Bob Alice\",\n            image: \"https://avatars.githubusercontent.com/u/67470890?s=200&v=4\",\n          }\n        }\n      },\n    })\n  )\n}\n\nexport const { handlers, auth } = NextAuth({\n  providers,\n})\n```\n\nThe above configuration example will add the GitHub provider all the time, and the `Credentials` provider only in development. After making that configuration tweak, we can write our `@playwright/test` tests just like above.\n\n```ts filename=\"tests/e2e/basic-auth.spec.ts\" {8}\nimport { test, expect, type Page } from \"@playwright/test\"\n\ntest(\"Basic auth\", async ({ page, browser }) => {\n  if (!process.env.TEST_PASSWORD) throw new TypeError(\"Missing TEST_PASSWORD\")\n\n  await test.step(\"should login\", async () => {\n    await page.goto(\"http://localhost:3000/auth/signin\")\n    await page.getByLabel(\"Password\").fill(process.env.TEST_PASSWORD)\n    await page.getByRole(\"button\", { name: \"Sign In\" }).click()\n    await page.waitForURL(\"http://localhost:3000\")\n    const session = await page.locator(\"pre\").textContent()\n\n    expect(JSON.parse(session ?? \"{}\")).toEqual({\n      user: {\n        email: \"bob@alice.com\",\n        name: \"Bob Alice\",\n        image: \"https://avatars.githubusercontent.com/u/67470890?s=200&v=4\",\n      },\n      expires: expect.any(String),\n    })\n  })\n\n  await test.step(\"should logout\", async () => {\n    await page.getByText(\"Sign out\").click()\n    await page\n      .locator(\"header\")\n      .getByRole(\"button\", { name: \"Sign in\", exact: true })\n      .waitFor()\n    await page.goto(\"http://localhost:3000/auth/session\")\n\n    expect(await page.locator(\"html\").textContent()).toBe(\"null\")\n  })\n})\n```\n"
  },
  {
    "path": "docs/pages/index.mdx",
    "content": "import Link from \"next/link\"\nimport { Pre, Code } from \"nextra/components\"\nimport Footer from \"@/components/Footer\"\nimport { RichTabs } from \"@/components/RichTabs\"\nimport { LogosMarquee } from \"@/components/LogosMarquee\"\nimport { Blur } from \"@/components/Blur\"\nimport { Guides } from \"@/components/Guides\"\nimport SvelteKit from \"../public/img/etc/sveltekit.svg\"\nimport Express from \"../public/img/etc/express.svg\"\nimport Next from \"../public/img/etc/nextjs.svg\"\nimport Qwik from \"../public/img/etc/qwik.svg\"\n\nimport { CaretRight } from \"@/icons\"\n\n<div className=\"relative overflow-hidden w-full h-full flex flex-col\">\n  <section className=\"flex justify-center w-full px-8 xl:px-0 flex-col lg:flex-row self-center items-center lg:items-start gap-6 max-w-7xl pt-12 pb-24 sm:py-32 lg:py-56\">\n    <div className=\"flex-col flex justify-start gap-10 lg:py-8 mx-8\">\n      <div className=\"flex flex-col gap-8 justify-center py-2 items-center w-full\">\n        <span className=\"text-6xl font-black lg:text-8xl\">Auth.js</span>\n        <div className=\"flex flex-col gap-4 justify-center text-2xl font-bold text-center lg:text-3xl\">\n          <div>Authentication for the Web.</div>\n          <div className=\"underline-highlight\">Free and open source.</div>\n        </div>\n      </div>\n      <div className=\"text-lg justify-center items-center flex gap-4\">\n        <Link\n          href=\"/getting-started\"\n          className=\"p-3 w-1/2 text-center bg-violet-600 dark:bg-violet-800 text-white hover:dark:bg-violet-900 transition hover:bg-violet-700 duration-300\"\n        >\n          Get Started\n        </Link>\n        <Link\n          href=\"https://github.com/nextauthjs/next-auth\"\n          target=\"_blank\"\n          className=\"p-3 w-1/2 justify-center flex gap-2 items-center dark:bg-gray-300 bg-violet-200 text-black transition duration-300 hover:dark:bg-gray-400 hover:bg-violet-300\"\n        >\n          <svg className=\"size-5\" viewBox=\"0 0 256 256\">\n            <rect width=\"256\" height=\"256\" fill=\"none\" />\n            <path\n              d=\"M119.83,56A52,52,0,0,0,76,32a51.92,51.92,0,0,0-3.49,44.7A49.28,49.28,0,0,0,64,104v8a48,48,0,0,0,48,48h48a48,48,0,0,0,48-48v-8a49.28,49.28,0,0,0-8.51-27.3A51.92,51.92,0,0,0,196,32a52,52,0,0,0-43.83,24Z\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n              strokeWidth=\"16\"\n            />\n            <path\n              d=\"M104,232V192a32,32,0,0,1,32-32h0a32,32,0,0,1,32,32v40\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n              strokeWidth=\"16\"\n            />\n            <path\n              d=\"M104,208H72a32,32,0,0,1-32-32A32,32,0,0,0,8,144\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n              strokeWidth=\"16\"\n            />\n          </svg>\n          Source\n        </Link>\n      </div>\n    </div>\n    <RichTabs\n      orientation=\"horizontal\"\n      tabKey=\"hero\"\n      defaultValue=\"nextjs\"\n      className=\"w-full flex flex-col !py-8 md:!py-16 !m-0 max-w-xl xl:max-w-2xl lg:self-start\"\n    >\n      <RichTabs.List className=\"justify-between p-2 rounded-t-lg bg-white/40 backdrop-blur dark:bg-neutral-950/25\">\n        <div className=\"flex gap-2\">\n          {[\n            { value: \"express\", src: Express,  name: \"Express\", darkInvert: true },\n            { value: \"nextjs\", src: Next, name: \"Next.js\", darkInvert: true },\n            { value: \"qwik\", src: Qwik,  name: \"Qwik\", darkInvert: false },\n            { value: \"sveltekit\", src: SvelteKit,  name: \"SvelteKit\", darkInvert: false },\n          ].map((trigger) => (\n            <RichTabs.Trigger\n              key={trigger.value}\n              title={trigger.name}\n              value={trigger.value}\n              orientation=\"vertical\"\n              className=\" !border-0 aria-selected:!bg-violet-600 aria-selected:dark:!bg-violet-900 aria-selected:!text-white dark:bg-neutral-800 bg-white !h-auto !w-auto !flex-row !gap-2 !justify-start p-2 px-3 rounded-md outline-none transition-all duration-300 hover:bg-violet-200 hover:dark:bg-violet-900/20 !font-normal\"\n            >\n              <img\n                width=\"24\"\n                src={trigger.src}\n                alt={`${trigger.name} Logo`}\n                className={trigger.darkInvert ? 'dark:invert': '' }\n              />\n            </RichTabs.Trigger>\n          ))}\n          <Link\n            href=\"/getting-started/integrations\"\n            className=\"!border-0 aria-selected:!bg-violet-600 aria-selected:dark:!bg-violet-900 dark:bg-neutral-900 bg-white !h-auto !w-auto !flex-row !gap-2 !justify-start p-2 px-3 rounded-md outline-none transition-all duration-300 flex items-center hover:bg-violet-200 hover:dark:bg-violet-900/20\"\n          >\n            <span className=\"hidden text-sm md:block\">More</span>\n            <CaretRight className=\"size-4\" />\n          </Link>\n        </div>\n        <div className=\"inline-flex gap-2 items-center self-end pr-4 h-12\">\n          <div className=\"bg-green-300 rounded-full size-4\"></div>\n          <div className=\"bg-amber-300 rounded-full size-4\"></div>\n          <div className=\"bg-red-300 rounded-full size-4\"></div>\n        </div>\n      </RichTabs.List>\n      <div className=\"p-2 pt-0 w-full rounded-b-lg shadow-md bg-white/40 backdrop-blur dark:bg-neutral-950/25 [&_div]:border-0 [&_div]:shadow-none [&>div.h-full>div>div>pre]:dark:!bg-neutral-950/60 [&>div.h-full>div>div>pre]:!bg-white/60\">\n      {Object.entries({\n        nextjs: `\\\n// auth.ts\nimport NextAuth from \"next-auth\"\nimport GitHub from \"next-auth/providers/github\"\nexport const { auth, handlers } = NextAuth({ providers: [GitHub] })\n\n// proxy.ts\nexport { auth as proxy } from \"@/auth\"\n\n// app/api/auth/[...nextauth]/route.ts\nimport { handlers } from \"@/auth\"\nexport const { GET, POST } = handlers\n`,\n          sveltekit: `\\\n// src/auth.ts\nimport { SvelteKitAuth } from \"@auth/sveltekit\"\nimport GitHub from '@auth/sveltekit/providers/github'\n\nexport const { handle } = SvelteKitAuth({\n  providers: [GitHub],\n})\n\n// src/hooks.server.ts\nexport { handle } from \"./auth\"\n`,\n          express: `\\\n// server.ts\nimport { express } from \"express\"\nimport { ExpressAuth } from \"@auth/express\"\nimport GitHub from \"@auth/express/providers/github\"\n\nconst app = express()\n\napp.use(\"/auth/\\*\", ExpressAuth({ providers: [GitHub] }))\n`,\n          qwik: `\\\n// src/routes/plugin@auth.ts\nimport { QwikAuth } from \"@auth/qwik\"\nimport GitHub from \"@auth/qwik/providers/github\"\nexport const { onRequest, useSession } = QwikAuth$(() => ({ providers: [GitHub] }))\n`\n}).map(([key, code]) =>\n\n  <RichTabs.Content\n    key={key}\n    orientation=\"vertical\"\n    value={key}\n    className=\"h-full\"\n    tabIndex={-1}\n  >\n    <Pre data-language=\"tsx\" data-copy=\"\">\n      <Code data-language=\"tsx\">\n        {code}\n      </Code>\n    </Pre>\n  </RichTabs.Content>\n)}\n</div>\n</RichTabs>\n\n  </section>\n  <Guides />\n  <section\n    className=\"flex relative h-[40rem] flex-col justify-between items-center pt-24 pb-12\n    bg-neutral-200 dark:bg-neutral-900 overflow-hidden\"\n  >\n    <div\n      className=\"z-10 flex gap-4 justify-start items-center text-2xl md:text-3xl font-bold\n      text-center text-white lg:text-4xl h-full\"\n    >\n      <div className=\"leading-normal text-slate-800 px-12 dark:text-slate-200 pb-12 dark:drop-shadow-none\">\n        Supports all these providers and more!\n      </div>\n    </div>\n    <LogosMarquee />\n  </section>\n  <Footer className=\"bg-white dark:bg-neutral-950 py-8 !m-0\" />\n  <Blur />\n  <div className=\"overflow-hidden bg-animation -z-10\">\n    <div id=\"stars\"></div>\n    <div id=\"stars2\"></div>\n    <div id=\"stars3\"></div>\n    <div id=\"stars4\"></div>\n  </div>\n</div>\n"
  },
  {
    "path": "docs/pages/reference/_meta.js",
    "content": "export default {\n  overview: \"Overview\",\n  \"--- frameworks\": {\n    type: \"separator\",\n    title: \"Frameworks\",\n  },\n  core: \"@auth/core\",\n  \"nextjs\": \"next-auth\",\n  sveltekit: \"@auth/sveltekit\",\n  express: \"@auth/express\",\n  qwik: \"@auth/qwik\",\n  \"solid-start\": \"@auth/solid-start\",\n  \"--- adapters\": {\n    type: \"separator\",\n    title: \"Adapters\",\n  },\n  \"prisma-adapter\": \"@auth/prisma-adapter\",\n  \"drizzle-adapter\": \"@auth/drizzle-adapter\",\n  \"azure-tables-adapter\": \"@auth/azure-tables-adapter\",\n  \"d1-adapter\": \"@auth/d1-adapter\",\n  \"dgraph-adapter\": \"@auth/dgraph-adapter\",\n  \"dynamodb-adapter\": \"@auth/dynamodb-adapter\",\n  \"edgedb-adapter\": \"@auth/edgedb-adapter\",\n  \"fauna-adapter\": \"@auth/fauna-adapter\",\n  \"firebase-adapter\": \"@auth/firebase-adapter\",\n  \"hasura-adapter\": \"@auth/hasura-adapter\",\n  \"kysely-adapter\": \"@auth/kysely-adapter\",\n  \"mikro-orm-adapter\": \"@auth/mikro-orm-adapter\",\n  \"mongodb-adapter\": \"@auth/mongodb-adapter\",\n  \"neo4j-adapter\": \"@auth/neo4j-adapter\",\n  \"neon-adapter\": \"@auth/neon-adapter\",\n  \"pg-adapter\": \"@auth/pg-adapter\",\n  \"pouchdb-adapter\": \"@auth/pouchdb-adapter\",\n  \"sequelize-adapter\": \"@auth/sequelize-adapter\",\n  \"supabase-adapter\": \"@auth/supabase-adapter\",\n  \"surrealdb-adapter\": \"@auth/surrealdb-adapter\",\n  \"typeorm-adapter\": \"@auth/typeorm-adapter\",\n  \"unstorage-adapter\": \"@auth/unstorage-adapter\",\n  \"upstash-redis-adapter\": \"@auth/upstash-redis-adapter\",\n  \"xata-adapter\": \"@auth/xata-adapter\",\n}\n"
  },
  {
    "path": "docs/pages/security.mdx",
    "content": "import { Callout, Steps } from \"nextra/components\"\nimport { ChatCircleText, GitBranch } from \"@/icons\"\n\n<h2 className=\"dark:border-primary-100/10 mt-10 flex flex-row items-center gap-2 border-b border-neutral-200/70 pb-1 text-3xl font-semibold tracking-tight text-slate-900 contrast-more:border-neutral-400 dark:text-slate-100 contrast-more:dark:border-neutral-400\">\n  <GitBranch className=\"size-8\" /> <span>Supported versions</span>\n</h2>\n\n- Security updates are only released for the current `latest` version.\n- Old releases are not maintained and do not receive updates.\n\n<Callout>\n  `@auth/*` packages (other than the database adapters) are currently under\n  development and - unless stated otherwise - they are not considered ready for\n  production yet. That said, we encourage you to reach out to us if you have any\n  questions or concerns via the below-mentioned channels. We are committed to\n  making Auth.js a secure and reliable solution for your authentication needs.\n</Callout>\n\n<h2 className=\"dark:border-primary-100/10 mt-10 flex flex-row items-center gap-2 border-b border-neutral-200/70 pb-1 text-3xl font-semibold tracking-tight text-slate-900 contrast-more:border-neutral-400 dark:text-slate-100 contrast-more:dark:border-neutral-400\">\n  <ChatCircleText className=\"size-8\" /> <span>Reporting a Vulnerability</span>\n</h2>\n\nAuth.js practices responsible disclosure. We request that you contact us directly to report serious issues that might impact the security of sites using Auth.js.\n\nIf you contact us regarding a serious issue:\n\n<Steps>\n\n### Getting back to you\n\nWe will endeavour to get back to you within 72 hours.\n\n### Publishing a fix\n\nWe will aim to publish a fix within 30 days.\n\n### Disclosing the issue\n\nWe will disclose the issue ( _and credit you, with your consent_ ) once a fix to resolve the issue has been released.\n\n### 90 days limit\n\nIf 90 days have elapsed and we still don't have a fix, we will disclose the issue publicly.\n\n</Steps>\n\nThe best way to report an issue is by contacting us via email at info@balazsorban.com, hi@thvu.dev,\nyo@ndo.dev and hi@ubbe.dev, or raise a public issue - **without disclosing any sensitive details** - requesting someone get in touch with you via whatever means you prefer for more details.\n\n<Callout type=\"info\">\n  For less serious issues (e.g. RFC compliance for unsupported flows or\n  potential issues that may cause a problem in the future) it is appropriate to\n  make these public as bug reports or feature requests or to raise a question to\n  open a discussion around them.\n</Callout>\n"
  },
  {
    "path": "docs/postcss.config.cjs",
    "content": "module.exports = {\n  plugins: {\n    \"tailwindcss/nesting\": {},\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "docs/public/.well-known/security.txt",
    "content": "Contact: mailto:info@balazsorban.com\nContact: mailto:hi@thvu.dev\nContact: mailto:authjs-security@ndo.dev\nAcknowledgments: https://authjs.dev/security\nPreferred-Languages: en\nCanonical: https://authjs.dev/.well-known/security.txt\n\n# Security Policy\n\nNextAuth.js practices responsible disclosure.\n\n## Reporting a Vulnerability\n\nWe request that you contact us directly to report serious issues that might impact the security of sites using NextAuth.js.\n\nIf you contact us regarding a serious issue:\n\n- We will endeavor to get back to you within 72 hours.\n- We will aim to publish a fix within 30 days.\n- We will disclose the issue (and credit you, with your consent) once a fix to resolve the issue has been released.\n- If 90 days has elapsed and we still don't have a fix, we will disclose the issue publicly.\n\nThe best way to report an issue is by contacting us via email at hi@thvu.dev, info@balazsorban.com and yo@ndo.dev, or raise a public issue requesting someone get in touch with you via whatever means you prefer for more details. (Please do not disclose sensitive details publicly at this stage.)\n\n> For less serious issues (e.g. RFC compliance for unsupported flows or potential issues that may cause a problem in the future) it is appropriate to submit these publicly as bug reports or feature requests or to raise a question to open a discussion around them.\n\n## Supported Versions\n\nSecurity updates are only released for the current version.\n\nOld releases are not maintained and do not receive updates.\n"
  },
  {
    "path": "docs/tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  darkMode: \"class\",\n  content: [\n    \"./theme.config.tsx\",\n    \"./pages/**/*.{js,jsx,ts,tsx,md,mdx}\",\n    \"./components/**/*.{js,jsx,ts,tsx,md,mdx}\",\n  ],\n  theme: {\n    extend: {\n      animation: {\n        blob: \"blob 12s infinite\",\n        orbit: \"orbit calc(var(--duration)*1s) linear infinite\",\n      },\n      keyframes: {\n        orbit: {\n          \"0%\": {\n            transform:\n              \"rotate(0deg) translateY(calc(var(--radius) * 1px)) rotate(0deg)\",\n          },\n          \"100%\": {\n            transform:\n              \"rotate(360deg) translateY(calc(var(--radius) * 1px)) rotate(-360deg)\",\n          },\n        },\n        blob: {\n          \"0%\": {\n            transform: \"translate(0px, 0px) scale(1)\",\n          },\n          \"33%\": {\n            transform: \"translate(125px, -120px) scale(1.2)\",\n          },\n          \"66%\": {\n            transform: \"translate(-90px, 70px) scale(0.8)\",\n          },\n          \"100%\": {\n            transform: \"translate(0px, 0px) scale(1)\",\n          },\n        },\n      },\n    },\n  },\n  plugins: [],\n}\n"
  },
  {
    "path": "docs/theme.config.tsx",
    "content": "import { DocsThemeConfig, ThemeSwitch } from \"nextra-theme-docs\"\nimport { Link } from \"@/components/Link\"\nimport { ChildrenProps } from \"@/utils/types\"\nimport Footer from \"@/components/Footer\"\nimport Docsearch from \"@/components/DocSearch\"\nimport dynamic from \"next/dynamic\"\nimport { usePathname } from \"next/navigation\"\nimport { useConfig } from \"nextra-theme-docs\"\n\nconst InkeepChatButton = dynamic(\n  () => import(\"@/components/InkeepSearch\").then((mod) => mod.InkeepTrigger),\n  {\n    ssr: false,\n    loading: () => (\n      <div className=\"hidden lg:block\">\n        <button className=\"flex items-center gap-2 rounded-lg bg-black/[.05] px-3 py-1.5 text-base leading-tight text-gray-800 transition-colors md:text-sm dark:bg-gray-50/10 dark:text-gray-200\">\n          <svg\n            xmlns=\"http://www.w3.org/2000/svg\"\n            width=\"16\"\n            height=\"16\"\n            fill=\"currentColor\"\n            viewBox=\"0 0 256 256\"\n          >\n            <path d=\"M197.58,129.06l-51.61-19-19-51.65a15.92,15.92,0,0,0-29.88,0L78.07,110l-51.65,19a15.92,15.92,0,0,0,0,29.88L78,178l19,51.62a15.92,15.92,0,0,0,29.88,0l19-51.61,51.65-19a15.92,15.92,0,0,0,0-29.88ZM140.39,163a15.87,15.87,0,0,0-9.43,9.43l-19,51.46L93,172.39A15.87,15.87,0,0,0,83.61,163h0L32.15,144l51.46-19A15.87,15.87,0,0,0,93,115.61l19-51.46,19,51.46a15.87,15.87,0,0,0,9.43,9.43l51.46,19ZM144,40a8,8,0,0,1,8-8h16V16a8,8,0,0,1,16,0V32h16a8,8,0,0,1,0,16H184V64a8,8,0,0,1-16,0V48H152A8,8,0,0,1,144,40ZM248,88a8,8,0,0,1-8,8h-8v8a8,8,0,0,1-16,0V96h-8a8,8,0,0,1,0-16h8V72a8,8,0,0,1,16,0v8h8A8,8,0,0,1,248,88Z\"></path>\n          </svg>\n          <span className=\"hidden md:inline xl:hidden\">AI</span>\n          <span className=\"hidden xl:inline\">Ask AI</span>\n        </button>\n      </div>\n    ),\n  }\n)\n\nconst config: DocsThemeConfig = {\n  logo: (\n    <div className=\"flex flex-row items-center\">\n      <img src=\"/img/etc/logo-sm.webp\" alt=\"Auth.js Logo\" width=\"30\" />\n      <span className=\"ml-2 text-xl font-black\">Auth.js</span>\n    </div>\n  ),\n  components: {\n    a: (props: ChildrenProps) => <Link href=\"\" {...props} />,\n  },\n  project: {\n    link: \"https://github.com/nextauthjs/next-auth\",\n    icon: null,\n  },\n  darkMode: false,\n  color: {\n    hue: {\n      light: 268,\n      dark: 280,\n    },\n    saturation: {\n      light: 100,\n      dark: 50,\n    },\n  },\n  search: {\n    component: <Docsearch />,\n  },\n  navbar: {\n    extraContent: (\n      <div className=\"flex !h-12 items-center md:gap-4 lg:-translate-x-4\">\n        <div className=\"hidden lg:block\">\n          <InkeepChatButton />\n        </div>\n        <div className=\"relative\">\n          <Link\n            className=\"hidden p-2 md:block\"\n            href=\"https://github.com/nextauthjs/next-auth\"\n            target=\"_blank\"\n          >\n            <svg\n              className=\"size-4\"\n              width=\"24\"\n              height=\"24\"\n              fill=\"currentColor\"\n              viewBox=\"3 3 18 18\"\n              xmlns=\"http://www.w3.org/2000/svg\"\n            >\n              <title>GitHub</title>\n              <path d=\"M12 3C7.0275 3 3 7.12937 3 12.2276C3 16.3109 5.57625 19.7597 9.15374 20.9824C9.60374 21.0631 9.77249 20.7863 9.77249 20.5441C9.77249 20.3249 9.76125 19.5982 9.76125 18.8254C7.5 19.2522 6.915 18.2602 6.735 17.7412C6.63375 17.4759 6.19499 16.6569 5.8125 16.4378C5.4975 16.2647 5.0475 15.838 5.80124 15.8264C6.51 15.8149 7.01625 16.4954 7.18499 16.7723C7.99499 18.1679 9.28875 17.7758 9.80625 17.5335C9.885 16.9337 10.1212 16.53 10.38 16.2993C8.3775 16.0687 6.285 15.2728 6.285 11.7432C6.285 10.7397 6.63375 9.9092 7.20749 9.26326C7.1175 9.03257 6.8025 8.08674 7.2975 6.81794C7.2975 6.81794 8.05125 6.57571 9.77249 7.76377C10.4925 7.55615 11.2575 7.45234 12.0225 7.45234C12.7875 7.45234 13.5525 7.55615 14.2725 7.76377C15.9937 6.56418 16.7475 6.81794 16.7475 6.81794C17.2424 8.08674 16.9275 9.03257 16.8375 9.26326C17.4113 9.9092 17.76 10.7281 17.76 11.7432C17.76 15.2843 15.6563 16.0687 13.6537 16.2993C13.98 16.5877 14.2613 17.1414 14.2613 18.0065C14.2613 19.2407 14.25 20.2326 14.25 20.5441C14.25 20.7863 14.4188 21.0746 14.8688 20.9824C16.6554 20.364 18.2079 19.1866 19.3078 17.6162C20.4077 16.0457 20.9995 14.1611 21 12.2276C21 7.12937 16.9725 3 12 3Z\"></path>\n            </svg>\n          </Link>\n          <div className=\"github-counter hidden lg:block\">22.2k</div>\n        </div>\n        <a\n          href=\"https://discord.authjs.dev/?utm_source=docs\"\n          title=\"Join our Discord\"\n          className=\"hidden transition hover:contrast-125 lg:block\"\n          target=\"_blank\"\n        >\n          <img\n            alt=\"Discord Logo\"\n            src=\"/img/providers/discord.svg\"\n            className=\"brightness-[0.85] grayscale dark:brightness-[1.7]\"\n            width=\"20\"\n          />\n        </a>\n        <ThemeSwitch\n          lite\n          className=\"!bg-transparent p-0 *:justify-center *:gap-0 [&_span]:hidden [&_svg]:size-4\"\n        />\n      </div>\n    ),\n  },\n  sidebar: {\n    defaultMenuCollapseLevel: 1,\n    toggleButton: false,\n  },\n  head: () => {\n    const pathname = usePathname()\n    const { frontMatter } = useConfig()\n    const url = `https://authjs.dev${pathname}`\n\n    const lastPathParam = pathname?.split(\"/\").at(-1)?.replaceAll(\"-\", \" \")\n    const capitalizedPathTitle = lastPathParam?.replace(/\\b\\w/g, (l) =>\n      l.toUpperCase()\n    )\n    const title = frontMatter.title\n      ? frontMatter.title\n      : capitalizedPathTitle\n        ? `Auth.js | ${capitalizedPathTitle}`\n        : \"Auth.js | Authentication for the Web\"\n\n    return (\n      <>\n        <link\n          rel=\"icon\"\n          href=\"/favicon-32x32.png\"\n          sizes=\"32x32\"\n          type=\"image/png\"\n        />\n        <link\n          rel=\"icon\"\n          href=\"/favicon-16x16.png\"\n          sizes=\"16x16\"\n          type=\"image/png\"\n        />\n        <title>{title}</title>\n        <meta property=\"og:url\" content={url} />\n        <meta property=\"og:title\" content={title} />\n        <meta\n          property=\"og:description\"\n          content={frontMatter.description || \"Authentication for the Web\"}\n        />\n        <meta\n          property=\"og:image\"\n          content={`https://authjs.dev/api/og?title=${encodeURIComponent(\n            title\n          )}`}\n        />\n      </>\n    )\n  },\n  banner: {\n    content: (\n      <>\n        The Auth.js project is now part of{` `}\n        <a\n          style={{ textDecoration: \"underline\" }}\n          href=\"https://better-auth.com/blog/authjs-joins-better-auth\"\n        >\n          <b>Better Auth</b>\n        </a>\n        .\n      </>\n    ),\n    dismissible: true,\n  },\n  editLink: {\n    content: \"Edit this page on GitHub →\",\n  },\n  feedback: {\n    content: \"Question? Give us feedback →\",\n    labels: \"feedback\",\n  },\n  toc: {\n    backToTop: true,\n  },\n  docsRepositoryBase: \"https://github.com/nextauthjs/next-auth/edit/main/docs\",\n  footer: {\n    component: <Footer />,\n  },\n}\n\nexport default config\n"
  },
  {
    "path": "docs/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": false,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"incremental\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/components/*\": [\"components/*\"],\n      \"@/utils/*\": [\"utils/*\"],\n      \"@/icons/*\": [\"components/Icons/*\"],\n      \"@/icons\": [\"components/Icons\"],\n      \"@/data/*\": [\"pages/data/*\"],\n      \"@/hooks/*\": [\"hooks/*\"]\n    },\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"strictNullChecks\": true\n  },\n  \"include\": [\n    \"next-env.d.ts\",\n    \"types.d.ts\",\n    \"**/*.ts\",\n    \"**/*.tsx\",\n    \"tailwind.config.js\",\n    \"pages/_meta.js\",\n    \".next/types/**/*.ts\"\n  ],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "docs/typedoc-nextauth.js",
    "content": "// @ts-check\n\nimport { MarkdownPageEvent } from \"typedoc-plugin-markdown\"\nimport path from \"path\"\nimport fs from \"fs\"\n\n/**\n * Local plugin to tweak TypeDoc output for nextra docs\n *\n *  @param {import(\"typedoc-plugin-markdown\").MarkdownApplication} app\n */\nexport function load(app) {\n  injectNextraCalloutImport(app)\n  parseOutput(app)\n  writeMetaJsFiles(app)\n}\n\n/**\n * Add nextra Callout component import to the top of each page\n *\n *  @param {import(\"typedoc-plugin-markdown\").MarkdownApplication} app\n */\nfunction injectNextraCalloutImport(app) {\n  const nextraCalloutImport = `import { Callout } from 'nextra/components';`\n  app.renderer.markdownHooks.on(\"page.begin\", () => nextraCalloutImport)\n  app.renderer.markdownHooks.on(\"index.page.begin\", () => nextraCalloutImport)\n}\n\n/**\n * - Parse Docusaurus style admonitions to Callout elements\n * - Parse Docusaurus style code block titles to MDX compatible code block titles\n *  @param {import(\"typedoc-plugin-markdown\").MarkdownApplication} app\n */\nfunction parseOutput(app) {\n  app.renderer.on(MarkdownPageEvent.END, (page) => {\n    const calloutRegex = /:::([^\\n\\s]*)([^\\n]*)([\\s\\S]*?):::/g\n    const codeBlockRegex = /(```(ts|js|sh|json)\\s)title=\"([^\"]*)\"/g\n\n    // map existing alert types to nextra\n    const calloutTypeMap = {\n      note: \"info\",\n      caution: \"warning\",\n      danger: \"error\",\n      tip: \"default\",\n    }\n\n    const replaceCallout = (match, p1, p2, p3) => {\n      const calloutType = calloutTypeMap[p1.trim()] || p1.trim()\n      const title = p2 ? `**${p2.trim()}** ` : \"\"\n      return `\n<Callout type=\"${calloutType}\">\n  ${title}${p3.trim()}\n</Callout>`\n    }\n\n    // replace ```ts title=\"xx\" with ```ts filename=\"xx\"\n    const replaceCodeBlockTitle = (match, p1, p2, p3) => `${p1}filename=\"${p3}\"`\n\n    page.contents = page.contents\n      ?.replace(calloutRegex, replaceCallout)\n      .replace(codeBlockRegex, replaceCodeBlockTitle)\n  })\n}\n\n/**\n * Writes Nextra _meta.js files to fix-up navigation labels.\n *\n *  @param {import(\"typedoc-plugin-markdown\").MarkdownApplication} app\n */\nfunction writeMetaJsFiles(app) {\n  app.renderer.postRenderAsyncJobs.push(async (output) => {\n    /**\n     *\n     * @param {import(\"typedoc-plugin-markdown\").NavigationItem[]} navigationItems\n     * @param {string} outputDirectory\n     * @param {Record<string,string>} defaultValue\n     */\n    const writeMetaJs = (\n      navigationItems,\n      outputDirectory,\n      defaultValue = {}\n    ) => {\n      const pages = defaultValue\n      navigationItems.forEach((item) => {\n        const pageKey = item.path ? path.parse(item.path).name : null\n        if (pageKey) {\n          pages[pageKey] = item.title\n          if (item?.children && item?.children?.length > 0) {\n            writeMetaJs(item.children, path.join(outputDirectory, pageKey), {})\n          }\n        }\n      })\n\n      // Rename generated 'next-auth' dir to 'nextjs'\n      if (new RegExp(\".*docs/pages/reference/nextjs$\").test(outputDirectory)) {\n        if (fs.existsSync(\"./pages/reference/nextjs\")) {\n          fs.rmdirSync(\"./pages/reference/nextjs\", { recursive: true })\n        }\n        fs.renameSync(\"./pages/reference/next-auth\", \"./pages/reference/nextjs\")\n      }\n\n      const metaJString = `\nexport default ${JSON.stringify(pages, null, 2)}`\n\n      if (new RegExp(\".*docs/pages/reference$\").test(outputDirectory)) return\n\n      fs.writeFileSync(path.join(outputDirectory, \"_meta.js\"), metaJString)\n    }\n\n    /**\n     * Recursively write _meta.js files for each page based on output.navigation\n     */\n    if (output.navigation) {\n      writeMetaJs(output.navigation, output.outputDirectory, {\n        overview: \"Overview\",\n      })\n    }\n  })\n}\n"
  },
  {
    "path": "docs/typedoc.config.cjs",
    "content": "// @ts-check\n\nconst fs = require(\"node:fs\")\nconst path = require(\"node:path\")\n\nconst isSkipAdapters = process.env.TYPEDOC_SKIP_ADAPTERS ? \"skip\" : \"adapter-\"\nconst excludePackages = new RegExp(\n  `(core|next-auth|frameworks-(?!template)|${isSkipAdapters})`\n)\n\nconst entryPoints = fs\n  .readdirSync(path.resolve(__dirname, \"../packages\"))\n  .filter((dir) => excludePackages.test(dir))\n  .map((dir) => `../packages/${dir}`)\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').PluginOptions}\n */\nmodule.exports = {\n  // typedoc options\n  entryPoints,\n  entryPointStrategy: \"packages\",\n  out: \"pages/reference\",\n  tsconfig: \"./tsconfig.json\",\n  plugin: [\n    \"typedoc-plugin-markdown\",\n    require.resolve(\"./typedoc-nextauth.js\"),\n    \"typedoc-plugin-mdn-links\",\n    \"typedoc-plugin-no-inherit\",\n  ],\n  disableSources: true,\n  excludeNotDocumented: true,\n  excludeExternals: true,\n  excludeInternal: true,\n  excludeProtected: true,\n  excludeReferences: true,\n  cleanOutputDir: false,\n  gitRevision: \"main\",\n  githubPages: false,\n  hideGenerator: true,\n  readme: \"none\",\n  sort: [\"kind\", \"static-first\", \"required-first\", \"alphabetical\"],\n  kindSortOrder: [\n    \"Function\",\n    \"TypeAlias\",\n    \"Interface\",\n    \"Reference\",\n    \"Project\",\n    \"Module\",\n    \"Namespace\",\n    \"Class\",\n    \"Constructor\",\n    \"Property\",\n    \"Variable\",\n    \"Accessor\",\n    \"Method\",\n    \"Parameter\",\n    \"TypeParameter\",\n    \"TypeLiteral\",\n    \"CallSignature\",\n    \"ConstructorSignature\",\n    \"IndexSignature\",\n    \"GetSignature\",\n    \"SetSignature\",\n  ],\n  name: \"API Reference\",\n\n  // typedoc-plugin-markdown options\n  outputFileStrategy: \"modules\",\n  entryFileName: \"overview.mdx\",\n  fileExtension: \".mdx\",\n  excludeScopesInPaths: true,\n  hidePageHeader: true,\n  hideBreadcrumbs: true,\n  excludeGroups: true,\n  expandObjects: true,\n  parametersFormat: \"table\",\n  indexFormat: \"table\",\n  useCodeBlocks: true,\n}\n"
  },
  {
    "path": "docs/types.d.ts",
    "content": "declare module '*.mdx' {\n  let MDXComponent: (props) => JSX.Element;\n  export default MDXComponent;\n}\n\ndeclare module '*.svg' {\n  let SVGComponent: (props) => JSX.Element;\n  export default SVGComponent;\n}\n\ntype TODO = any\n"
  },
  {
    "path": "docs/utils/types.ts",
    "content": "export interface ChildrenProps {\n  children: React.ReactNode\n}\n"
  },
  {
    "path": "docs/utils/useCopyButton.ts",
    "content": "import {\n  useState,\n  useRef,\n  useEffect,\n  useCallback,\n  type MouseEventHandler,\n} from \"react\"\n\nexport function useCopyButton(\n  onCopy: () => void\n): [checked: boolean, onClick: MouseEventHandler] {\n  const [checked, setChecked] = useState(false)\n  const timeoutRef = useRef<number | null>(null)\n\n  const onClick: MouseEventHandler = useCallback(() => {\n    if (timeoutRef.current) window.clearTimeout(timeoutRef.current)\n    timeoutRef.current = window.setTimeout(() => {\n      setChecked(false)\n    }, 1500)\n    onCopy()\n    setChecked(true)\n  }, [onCopy])\n\n  // Avoid updates after being unmounted\n  useEffect(() => {\n    return () => {\n      if (timeoutRef.current) window.clearTimeout(timeoutRef.current)\n    }\n  }, [])\n\n  return [checked, onClick]\n}\n"
  },
  {
    "path": "docs/utils/useInkeepSettings.ts",
    "content": "import type {\n  InkeepAIChatSettings,\n  InkeepWidgetBaseSettings,\n  InkeepModalSettings,\n  AIChatDisclaimerSettings,\n} from \"@inkeep/widgets\"\nimport { useTheme } from \"nextra-theme-docs\"\n\ntype InkeepSharedSettings = {\n  baseSettings: InkeepWidgetBaseSettings\n  aiChatSettings: InkeepAIChatSettings\n  modalSettings: InkeepModalSettings\n}\n\nconst useInkeepSettings = (): InkeepSharedSettings => {\n  const { resolvedTheme } = useTheme()\n  const baseSettings: InkeepWidgetBaseSettings = {\n    apiKey: process.env.NEXT_PUBLIC_INKEEP_API_KEY!,\n    integrationId: process.env.NEXT_PUBLIC_INKEEP_INTEGRATION_ID!,\n    organizationId: process.env.NEXT_PUBLIC_INKEEP_ORGANIZATION_ID!,\n    primaryBrandColor: \"#efe0ff\", // your brand color, widget color scheme is derived from this\n    organizationDisplayName: \"Auth.js\",\n    theme: {\n      colorMode: {\n        forcedColorMode: resolvedTheme, // to sync dark mode with the widget\n      },\n    },\n  }\n\n  const modalSettings: InkeepModalSettings = {\n    defaultView: \"AI_CHAT\",\n    switchToChatMessage: \"Switch to chat\",\n  }\n\n  const disclaimerSettings: AIChatDisclaimerSettings = {\n    isDisclaimerEnabled: true,\n    disclaimerLabel: \"Usage policy\",\n    disclaimerTooltip:\n      \"Your data is <b>never used to train the underlying LLM models</b>. Information provided by this AI assistant is <b>not guaranteed to be accurate or comprehensive</b>. Please consult Auth.js's documentation and GitHub repository for <b>authoritative results</b> if you are unsure. More information about how this data is used can be found in InKeep's <a href='https://docs.inkeep.com/overview/privacy'>privacy page</a>.\",\n  }\n\n  const aiChatSettings: InkeepAIChatSettings = {\n    botAvatarSrcUrl: \"/img/etc/logo-sm.webp\",\n    quickQuestions: [\n      \"How do I migrate my Next.js app from v4 to v5?\",\n      \"How do I save extra fields from a provider's user profile response?\",\n      \"How do I access the session object in SvelteKit?\",\n    ],\n    disclaimerSettings,\n  }\n\n  return { baseSettings, aiChatSettings, modalSettings }\n}\n\nexport default useInkeepSettings\n"
  },
  {
    "path": "docs/vercel.json",
    "content": "{\n  \"$schema\": \"https://openapi.vercel.sh/vercel.json\",\n  \"cleanUrls\": true,\n  \"headers\": [\n    {\n      \"source\": \"/(.*)\",\n      \"headers\": [\n        { \"key\": \"X-Content-Type-Options\", \"value\": \"nosniff\" },\n        { \"key\": \"X-Frame-Options\", \"value\": \"DENY\" },\n        { \"key\": \"X-XSS-Protection\", \"value\": \"1; mode=block\" },\n        {\n          \"key\": \"Strict-Transport-Security\",\n          \"value\": \"max-age=31536000; includeSubDomains; preload\"\n        }\n      ]\n    }\n  ],\n  \"crons\": [\n    {\n      \"path\": \"/api/cron\",\n      \"schedule\": \"42 */2 * * *\"\n    }\n  ]\n}\n"
  },
  {
    "path": "eslint.config.mjs",
    "content": "import globals from \"globals\"\nimport jsdoc from \"eslint-plugin-jsdoc\"\nimport eslintConfigPrettier from \"eslint-config-prettier\"\nimport eslintPluginSvelte from \"eslint-plugin-svelte\"\nimport js from \"@eslint/js\"\nimport tsParser from \"@typescript-eslint/parser\"\nimport tsEslint from \"typescript-eslint\"\nimport reactRecommended from \"eslint-plugin-react/configs/recommended.js\"\nimport reactJsxRuntime from \"eslint-plugin-react/configs/jsx-runtime.js\"\nimport svelteParser from \"svelte-eslint-parser\"\nimport pluginImportX from \"eslint-plugin-import-x\"\nimport { includeIgnoreFile } from \"@eslint/compat\"\nimport path from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = path.dirname(__filename)\nconst gitignorePath = path.resolve(__dirname, \".gitignore\")\n\nexport default tsEslint.config(\n  js.configs.recommended,\n  ...tsEslint.configs.recommended,\n  eslintConfigPrettier,\n  {\n    name: \"React\",\n    files: [\"**/*.{ts,tsx,jsx}\"],\n    ...reactRecommended,\n    ...reactJsxRuntime,\n    languageOptions: {\n      ...reactRecommended.languageOptions,\n      globals: {\n        ...globals.serviceworker,\n        ...globals.browser,\n      },\n    },\n  },\n  {\n    name: \"TypeScript\",\n    files: [\"**/*.ts\", \"**/*.tsx\"],\n    languageOptions: {\n      parser: tsEslint.parser,\n      parserOptions: {\n        projectService: true,\n        project: [\"./packages/utils/tsconfig.eslint.json\"],\n      },\n      globals: {\n        ...globals.browser,\n        ...globals.node,\n      },\n    },\n    settings: {\n      react: {\n        version: \"18\",\n      },\n    },\n    rules: {\n      \"prefer-const\": [\"error\", { destructuring: \"all\" }],\n      \"no-empty\": [\"error\", { allowEmptyCatch: true }],\n      \"@typescript-eslint/no-floating-promises\": \"error\",\n      \"@typescript-eslint/no-explicit-any\": \"warn\",\n      \"@typescript-eslint/ban-ts-comment\": \"warn\",\n      \"@typescript-eslint/no-empty-object-type\": [\n        \"error\",\n        {\n          allowInterfaces: \"with-single-extends\",\n        },\n      ],\n      \"@typescript-eslint/explicit-function-return-type\": \"off\",\n      \"@typescript-eslint/method-signature-style\": \"off\",\n      \"@typescript-eslint/naming-convention\": \"off\",\n      \"@typescript-eslint/no-non-null-assertion\": \"off\",\n      \"@typescript-eslint/restrict-template-expressions\": \"off\",\n      \"@typescript-eslint/strict-boolean-expressions\": \"off\",\n      \"@typescript-eslint/no-unused-vars\": [\n        \"error\",\n        {\n          args: \"all\",\n          argsIgnorePattern: \"^_\",\n          caughtErrors: \"all\",\n          caughtErrorsIgnorePattern: \"^_\",\n          destructuredArrayIgnorePattern: \"^_\",\n          varsIgnorePattern: \"^_\",\n          ignoreRestSiblings: true,\n        },\n      ],\n      \"react/prop-types\": \"off\",\n      \"react/no-unescaped-entities\": \"off\",\n    },\n    plugins: {\n      \"import-x\": pluginImportX,\n    },\n  },\n  {\n    name: \"JSDoc\",\n    files: [\"packages/{core,sveltekit}/*.ts\"],\n    ignores: [\"**/*.d.ts\"],\n    languageOptions: {\n      parser: tsEslint.parser,\n    },\n    plugins: {\n      jsdoc,\n    },\n    rules: {\n      \"jsdoc/tag-lines\": \"off\",\n      \"jsdoc/require-param\": \"off\",\n      \"jsdoc/require-returns\": \"off\",\n      \"jsdoc/require-jsdoc\": [\n        \"warn\",\n        {\n          publicOnly: true,\n          enableFixer: false,\n        },\n      ],\n      \"jsdoc/no-multi-asterisks\": [\n        \"warn\",\n        {\n          allowWhitespace: true,\n        },\n      ],\n    },\n  },\n  {\n    name: \"SvelteKit\",\n    files: [\"**/*.svelte\"],\n    ...eslintPluginSvelte.configs[\"flat/recommended\"].rules,\n    languageOptions: {\n      globals: {\n        ...globals.browser,\n        ...globals.node,\n      },\n      ecmaVersion: 2020,\n      sourceType: \"module\",\n      parser: svelteParser,\n      parserOptions: {\n        parser: tsParser,\n        extraFileExtensions: [\".svelte\"],\n      },\n    },\n  },\n  {\n    name: \"Global Ignores\",\n    ignores: [\n      ...includeIgnoreFile(gitignorePath).ignores,\n      \"**/.*\", // dotfiles aren't ignored by default in FlatConfig\n      \".*\", // dotfiles aren't ignored by default in FlatConfig\n      \".eslintrc.js\",\n      \".cache-loader\",\n      \".DS_Store\",\n      \".pnpm-debug.log\",\n      \".turbo\",\n      \".vscode/generated*\",\n      \"/_work\",\n      \"/actions-runner\",\n      \"node_modules\",\n      \"patches\",\n      \"pnpm-lock.yaml\",\n      \".github/actions/issue-validator/index.mjs\",\n      \"**/*.cjs\",\n      \"**/*.js\",\n      \"**/*.d.ts\",\n      \"**/*.d.ts.map\",\n      \".svelte-kit\",\n      \".next\",\n      \".nuxt\",\n      \"build\",\n      \"static\",\n      \"coverage\",\n      \"dist\",\n      \"packages/core/src/providers/provider-types.ts\",\n      \"packages/core/src/lib/pages/styles.ts\",\n      \"packages/frameworks-sveltekit/package\",\n      \"packages/frameworks-sveltekit/vite.config.{js,ts}.timestamp-*\",\n      \".branches\",\n      \"db.sqlite\",\n      \"dev.db\",\n      \"dynamodblocal-bin\",\n      \"firebase-debug.log\",\n      \"firestore-debug.log\",\n      \"migrations\",\n      \"test.schema.gql\",\n      \"apps\",\n      \"packages/**/*test*\",\n      \"docs/**\",\n    ],\n  }\n)\n"
  },
  {
    "path": "lefthook.yml",
    "content": "pre-commit:\n  parallel: true\n  commands:\n    format:\n      run: pnpm prettier --cache --write {staged_files}\n      stage_fixed: true\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"root\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"repository\": \"https://github.com/nextauthjs/next-auth.git\",\n  \"scripts\": {\n    \"build:app\": \"turbo run build --filter=next-auth-app\",\n    \"build:docs\": \"turbo run build --filter=docs\",\n    \"build\": \"turbo run build --filter=next-auth --filter=@auth/*\",\n    \"test\": \"turbo run test --concurrency=1 --filter=[HEAD^1] --filter=./packages/* --filter=!*app* --filter=!*dynamo* --filter=!*edgedb* --filter=!*hasura* --filter=!*mikro* --filter=!*dgraph* --filter=!*xata* --filter=!*typeorm*\",\n    \"test:e2e\": \"turbo run test:e2e --filter=next-auth\",\n    \"test:e2e:watch\": \"turbo run test:e2e -- --ui\",\n    \"clean\": \"turbo run clean --no-cache\",\n    \"dev\": \"pnpm dev:next\",\n    \"dev:next\": \"turbo run dev --parallel --continue --filter=next-auth-app... --filter=@auth/core --filter=!./packages/adapter-*\",\n    \"dev:e2e:next\": \"turbo run dev --filter=next-auth-app\",\n    \"dev:db\": \"turbo run dev --parallel --continue --filter=next-auth-app...\",\n    \"dev:sveltekit\": \"turbo run dev --parallel --continue --filter=sveltekit-auth-app...\",\n    \"dev:express\": \"turbo run dev --parallel --continue --filter=express-auth-app...\",\n    \"dev:qwik\": \"turbo run dev --parallel --continue --filter=qwik-auth-app...\",\n    \"dev:docs\": \"turbo run dev --filter=docs\",\n    \"email\": \"fake-smtp-server\",\n    \"lint\": \"eslint --cache .\",\n    \"format\": \"prettier --cache --check .\",\n    \"format:write\": \"prettier --cache --write .\",\n    \"release\": \"release\",\n    \"peek\": \"pnpm release --peek\",\n    \"setup-fw-integration\": \"pnpm clean --filter=@auth/frameworks-template && node packages/utils/scripts/setup-fw-integration.js\"\n  },\n  \"devDependencies\": {\n    \"@actions/core\": \"^1.10.0\",\n    \"@balazsorban/monorepo-release\": \"0.5.1\",\n    \"@eslint/compat\": \"^1.1.1\",\n    \"@eslint/js\": \"^9.9.1\",\n    \"@playwright/test\": \"1.40.0\",\n    \"@types/node\": \"^20.8.10\",\n    \"@typescript-eslint/eslint-plugin\": \"v6.19.1\",\n    \"@typescript-eslint/parser\": \"v6.19.1\",\n    \"@vitest/coverage-v8\": \"1.2.1\",\n    \"@vitest/ui\": \"^1.2.2\",\n    \"eslint\": \"9.9.1\",\n    \"eslint-config-prettier\": \"^8.10.0\",\n    \"eslint-plugin-import-x\": \"^4.1.1\",\n    \"eslint-plugin-jsdoc\": \"^39.9.1\",\n    \"eslint-plugin-promise\": \"^6.0.0\",\n    \"eslint-plugin-react\": \"^7.33.2\",\n    \"eslint-plugin-svelte\": \"^2.38.0\",\n    \"fake-smtp-server\": \"^0.8.0\",\n    \"lefthook\": \"1.7.15\",\n    \"globals\": \"^15.9.0\",\n    \"prettier\": \"3.3.3\",\n    \"prettier-plugin-svelte\": \"^3.2.6\",\n    \"prettier-plugin-tailwindcss\": \"^0.6.6\",\n    \"svelte-eslint-parser\": \"^0.35.0\",\n    \"turbo\": \"^2.1.1\",\n    \"typescript\": \"5.3.3\",\n    \"typescript-eslint\": \"^8.3.0\",\n    \"utils\": \"workspace:*\",\n    \"vite\": \"^5.1.8\",\n    \"vitest\": \"1.2.2\"\n  },\n  \"engines\": {\n    \"node\": \"^18.18.0 || ^20.8.0 || ^22.0.0\"\n  },\n  \"packageManager\": \"pnpm@9.2.0+sha512.98a80fd11c2e7096747762304106432b3ddc67dcf54b5a8c01c93f68a2cd5e05e6821849522a06fb76284d41a2660d5e334f2ee3bbf29183bf2e739b1dafa771\",\n  \"prettier\": {\n    \"semi\": false,\n    \"singleQuote\": false,\n    \"trailingComma\": \"es5\",\n    \"plugins\": [\n      \"prettier-plugin-svelte\",\n      \"prettier-plugin-tailwindcss\"\n    ]\n  },\n  \"pnpm\": {\n    \"overrides\": {\n      \"mailparser\": \"3.6.6\"\n    },\n    \"patchedDependencies\": {\n      \"@balazsorban/monorepo-release@0.5.1\": \"patches/@balazsorban__monorepo-release@0.5.1.patch\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/adapter-azure-tables/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://azure.microsoft.com/en-us/products/storage/tables\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/azure-tables.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Azure Table Storage Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/azure-tables-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/azure-tables-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/azure-tables-adapter?color=green&label=@auth/azure-tables-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/azure-tables-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/azure-tables-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/getting-started/adapters/azure-tables).\n"
  },
  {
    "path": "packages/adapter-azure-tables/package.json",
    "content": "{\n  \"name\": \"@auth/azure-tables-adapter\",\n  \"version\": \"1.11.0\",\n  \"description\": \"Azure Tables Storage adapter for next-auth.\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Nikita Dmitrijev <nikitadmitry@gmail.com>\",\n  \"contributors\": [\n    \"Thang Huu Vu <hi@thvu.dev>\"\n  ],\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"azure-tables\",\n    \"adapter\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"files\": [\n    \"*.d.ts*\",\n    \"*.js\",\n    \"src\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"test\": \"./test/test.sh\",\n    \"build\": \"tsc\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"@azure/data-tables\": \"^13.2.1\"\n  },\n  \"devDependencies\": {\n    \"@azure/data-tables\": \"^13.2.1\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-azure-tables/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: \"16px\"}}>\n *  <p>An official <a href=\"https://azure.microsoft.com/en-us/products/storage/tables\">Azure Table Storage</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://azure.microsoft.com/en-us/products/storage/tables\">\n *   <img style={{display: \"block\"}} src=\"/img/adapters/azure-tables.svg\" width=\"48\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install next-auth @auth/azure-tables-adapter\n * ```\n *\n * @module @auth/azure-tables-adapter\n */\n\nimport type {\n  Adapter,\n  AdapterUser,\n  AdapterAccount,\n  AdapterSession,\n  VerificationToken,\n} from \"@auth/core/adapters\"\nimport {\n  GetTableEntityResponse,\n  TableClient,\n  TableEntityResult,\n} from \"@azure/data-tables\"\n\nexport const keys = {\n  user: \"user\",\n  userByEmail: \"userByEmail\",\n  account: \"account\",\n  accountByUserId: \"accountByUserId\",\n  session: \"session\",\n  sessionByUserId: \"sessionByUserId\",\n  verificationToken: \"verificationToken\",\n}\n\nexport function withoutKeys<T>(\n  entity: GetTableEntityResponse<TableEntityResult<T>>\n): T {\n  delete entity.partitionKey\n  delete entity.rowKey\n  // @ts-expect-error\n  delete entity.etag\n  delete entity.timestamp\n  // @ts-expect-error\n  delete entity[\"odata.metadata\"]\n\n  return entity\n}\n\nexport const TableStorageAdapter = (client: TableClient): Adapter => {\n  return {\n    async createUser(user) {\n      const id = crypto.randomUUID()\n      const newUser = {\n        ...user,\n        id,\n      }\n      await Promise.all([\n        client.createEntity({\n          ...newUser,\n          partitionKey: keys.userByEmail,\n          rowKey: user.email,\n        }),\n        client.createEntity({\n          ...newUser,\n          partitionKey: keys.user,\n          rowKey: id,\n        }),\n      ])\n      return newUser\n    },\n    async getUser(id: string) {\n      try {\n        const user = await client.getEntity<AdapterUser>(keys.user, id)\n        return withoutKeys(user)\n      } catch {\n        return null\n      }\n    },\n    async getUserByEmail(email) {\n      try {\n        const user = await client.getEntity<AdapterUser>(\n          keys.userByEmail,\n          email\n        )\n        return withoutKeys(user)\n      } catch {\n        return null\n      }\n    },\n    async getUserByAccount({ providerAccountId, provider }) {\n      try {\n        const rowKey = `${providerAccountId}_${provider}`\n        const account = await client.getEntity<AdapterAccount>(\n          keys.account,\n          rowKey\n        )\n        const user = await client.getEntity<AdapterUser>(\n          keys.user,\n          account.userId\n        )\n        return withoutKeys(user)\n      } catch {\n        return null\n      }\n    },\n    async updateUser(user) {\n      const _user = await client.getEntity<AdapterUser>(keys.user, user.id)\n      const updatedUser = {\n        ...user,\n        partitionKey: keys.user,\n        rowKey: _user.id,\n      }\n      await client.updateEntity(updatedUser, \"Merge\")\n      return { ..._user, ...updatedUser }\n    },\n    async deleteUser(userId) {\n      try {\n        const user = await client.getEntity<AdapterUser>(keys.user, userId)\n        const { sessionToken } = await client.getEntity<AdapterSession>(\n          keys.sessionByUserId,\n          userId\n        )\n        const accounts = withoutKeys(\n          await client.getEntity<AdapterAccount>(keys.accountByUserId, userId)\n        )\n        const deleteAccounts = Object.keys(accounts).map((property) =>\n          client.deleteEntity(keys.account, `${accounts[property]}_${property}`)\n        )\n        await Promise.allSettled([\n          client.deleteEntity(keys.userByEmail, user.email),\n          client.deleteEntity(keys.user, userId),\n          client.deleteEntity(keys.session, sessionToken),\n          client.deleteEntity(keys.sessionByUserId, userId),\n          ...deleteAccounts,\n          client.deleteEntity(keys.accountByUserId, userId),\n        ])\n        return withoutKeys(user)\n      } catch {\n        return null\n      }\n    },\n    async linkAccount(account) {\n      try {\n        await client.createEntity({\n          ...account,\n          partitionKey: keys.account,\n          rowKey: `${account.providerAccountId}_${account.provider}`,\n        })\n        await client.upsertEntity({\n          partitionKey: keys.accountByUserId,\n          rowKey: account.userId,\n          [account.provider]: account.providerAccountId,\n        })\n        return account\n      } catch {\n        return null\n      }\n    },\n    async unlinkAccount({ providerAccountId, provider }) {\n      const rowKey = `${providerAccountId}_${provider}`\n      const account = await client.getEntity<AdapterAccount>(\n        keys.account,\n        rowKey\n      )\n      await client.deleteEntity(keys.account, rowKey)\n      await client.deleteEntity(keys.accountByUserId, account.userId)\n    },\n    async createSession(session) {\n      await client.createEntity({\n        ...session,\n        partitionKey: keys.session,\n        rowKey: session.sessionToken,\n      })\n      await client.upsertEntity({\n        partitionKey: keys.sessionByUserId,\n        rowKey: session.userId,\n        sessionToken: session.sessionToken,\n      })\n      return session\n    },\n    async getSessionAndUser(sessionToken) {\n      try {\n        const session = await client.getEntity<AdapterSession>(\n          keys.session,\n          sessionToken\n        )\n        if (session.expires.valueOf() < Date.now()) {\n          await client.deleteEntity(keys.session, sessionToken)\n        }\n        const user = await client.getEntity<AdapterUser>(\n          keys.user,\n          session.userId\n        )\n        return {\n          session: withoutKeys(session),\n          user: withoutKeys(user),\n        }\n      } catch {\n        return null\n      }\n    },\n    async updateSession(session) {\n      const _session = await client.getEntity<AdapterSession>(\n        keys.session,\n        session.sessionToken\n      )\n      const newSession = {\n        expires: session.expires ?? _session.expires,\n      }\n      await client.updateEntity({\n        ...newSession,\n        partitionKey: keys.session,\n        rowKey: session.sessionToken,\n      })\n      return { ...withoutKeys(_session), ...newSession }\n    },\n    async deleteSession(sessionToken) {\n      try {\n        const session = await client.getEntity<AdapterSession>(\n          keys.session,\n          sessionToken\n        )\n        await Promise.allSettled([\n          client.deleteEntity(keys.session, sessionToken),\n          client.deleteEntity(keys.sessionByUserId, session.userId),\n        ])\n        return withoutKeys(session)\n      } catch {\n        return null\n      }\n    },\n    async createVerificationToken(token) {\n      await client.createEntity({\n        ...token,\n        partitionKey: keys.verificationToken,\n        rowKey: token.token,\n      })\n      return token\n    },\n    async useVerificationToken({ identifier, token }) {\n      try {\n        const tokenEntity = await client.getEntity<VerificationToken>(\n          keys.verificationToken,\n          token\n        )\n        if (tokenEntity.identifier !== identifier) {\n          return null\n        }\n        await client.deleteEntity(keys.verificationToken, token)\n        return withoutKeys(tokenEntity)\n      } catch {\n        return null\n      }\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-azure-tables/test/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport {\n  AzureNamedKeyCredential,\n  TableServiceClient,\n  TableClient,\n} from \"@azure/data-tables\"\nimport { keys, TableStorageAdapter, withoutKeys } from \"../src\"\nimport type { AdapterUser, VerificationToken } from \"@auth/core/adapters\"\n\nconst testAccount = {\n  // default constants used by a dev instance of azurite\n  name: \"devstoreaccount1\",\n  key: \"Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==\",\n  tableEndpoint: \"http://127.0.0.1:10002/devstoreaccount1\",\n}\n\nconst authTableName = \"authTest\"\n\nconst credential = new AzureNamedKeyCredential(\n  testAccount.name,\n  testAccount.key\n)\n\nconst authClient = new TableClient(\n  testAccount.tableEndpoint,\n  authTableName,\n  credential,\n  { allowInsecureConnection: true }\n)\n\nrunBasicTests({\n  adapter: TableStorageAdapter(authClient),\n  db: {\n    async connect() {\n      const serviceClient = new TableServiceClient(\n        testAccount.tableEndpoint,\n        credential,\n        { allowInsecureConnection: true }\n      )\n      await serviceClient.createTable(authTableName)\n    },\n    async user(id) {\n      try {\n        const userById = await authClient.getEntity<AdapterUser>(keys.user, id)\n\n        return withoutKeys(userById)\n      } catch (e) {\n        console.error(e)\n        return null\n      }\n    },\n    async account(provider_providerAccountId) {\n      try {\n        const account = await authClient.getEntity(\n          keys.account,\n          `${provider_providerAccountId.providerAccountId}_${provider_providerAccountId.provider}`\n        )\n\n        return withoutKeys(account)\n      } catch {\n        return null\n      }\n    },\n    async session(sessionToken) {\n      try {\n        const session = await authClient.getEntity(keys.session, sessionToken)\n\n        return withoutKeys(session)\n      } catch {\n        return null\n      }\n    },\n    async verificationToken(identifier_token) {\n      try {\n        const verificationToken = await authClient.getEntity<VerificationToken>(\n          keys.verificationToken,\n          identifier_token.token\n        )\n\n        if (verificationToken.identifier !== identifier_token.identifier) {\n          return null\n        }\n\n        return withoutKeys(verificationToken)\n      } catch {\n        return null\n      }\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-azure-tables/test/test.sh",
    "content": "#!/usr/bin/env bash\n\nCONTAINER_NAME=authjs-azure-tables-test\n\n# Start db\ndocker run -d --rm \\\n  -p 10002:10002 \\\n  --name ${CONTAINER_NAME} \\\n  mcr.microsoft.com/azure-storage/azurite azurite-table -l /workspace -d /workspace/debug.log --tableHost 0.0.0.0 --loose\n\necho \"Waiting 10s for db to start...\"\nsleep 10\n\n# Always stop container, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  docker stop ${CONTAINER_NAME}\nelse\n  docker stop ${CONTAINER_NAME} && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-azure-tables/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-azure-tables/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/azure-tables-adapter\",\n  entryFileName: \"../azure-tables-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-d1/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://developers.cloudflare.com/d1/\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/d1.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Cloudflare D1 Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/drizzle-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/d1-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/d1-adapter?color=green&label=@auth/d1-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/d1-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/d1-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/d1).\n"
  },
  {
    "path": "packages/adapter-d1/package.json",
    "content": "{\n  \"name\": \"@auth/d1-adapter\",\n  \"version\": \"1.11.0\",\n  \"description\": \"A Cloudflare D1 adapter for Auth.js\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Josh Schlesser <josh@schlesser.dev>\",\n  \"contributors\": [\n    \"Thang Huu Vu <hi@thvu.dev>\"\n  ],\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"@auth\",\n    \"Auth.js\",\n    \"next.js\",\n    \"oauth\",\n    \"d1\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"files\": [\n    \"*.d.ts*\",\n    \"*.js\",\n    \"src\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"tsc\",\n    \"test\": \"vitest -c ../utils/vitest.config.ts\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"@cloudflare/workers-types\": \"^4.20230321.0\",\n    \"@miniflare/d1\": \"^2.12.2\",\n    \"better-sqlite3\": \"^9.6.0\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-d1/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: \"16px\"}}>\n *  <p>An official <a href=\"https://developers.cloudflare.com/d1/\">Cloudflare D1</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://developers.cloudflare.com/d1/\">\n *   <img style={{display: \"block\"}} src=\"/img/adapters/d1.svg\" width=\"48\" />\n *  </a>\n * </div>\n *\n * ## Warning\n * This adapter is not developed or maintained by Cloudflare and they haven't declared the D1 api stable.  The author will make an effort to keep this adapter up to date.\n * The adapter is compatible with the D1 api as of March 22, 2023.\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install next-auth @auth/d1-adapter\n * ```\n *\n * @module @auth/d1-adapter\n */\n\nimport type { D1Database as WorkerDatabase } from \"@cloudflare/workers-types\"\nimport type { D1Database as MiniflareD1Database } from \"@miniflare/d1\"\nimport {\n  type Adapter,\n  type AdapterSession,\n  type AdapterUser,\n  type AdapterAccount,\n  type VerificationToken as AdapterVerificationToken,\n  isDate,\n} from \"@auth/core/adapters\"\nimport {\n  CREATE_ACCOUNT_SQL,\n  CREATE_SESSION_SQL,\n  CREATE_USER_SQL,\n  CREATE_VERIFICATION_TOKEN_SQL,\n  DELETE_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,\n  DELETE_ACCOUNT_BY_USER_ID_SQL,\n  DELETE_SESSION_BY_USER_ID_SQL,\n  DELETE_SESSION_SQL,\n  DELETE_USER_SQL,\n  DELETE_VERIFICATION_TOKEN_SQL,\n  GET_ACCOUNT_BY_ID_SQL,\n  GET_SESSION_BY_TOKEN_SQL,\n  GET_USER_BY_ACCOUNTL_SQL,\n  GET_USER_BY_EMAIL_SQL,\n  GET_USER_BY_ID_SQL,\n  GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,\n  UPDATE_SESSION_BY_SESSION_TOKEN_SQL,\n  UPDATE_USER_BY_ID_SQL,\n} from \"./queries.js\"\n\nexport { up } from \"./migrations.js\"\n\n/**\n * @type @cloudflare/workers-types.D1Database | @miniflare/d1.D1Database\n */\nexport type D1Database = WorkerDatabase | MiniflareD1Database\n\n// format is borrowed from the supabase adapter, graciously\nfunction format<T>(obj: Record<string, any>): T {\n  for (const [key, value] of Object.entries(obj)) {\n    if (value === null) {\n      delete obj[key]\n    }\n\n    if (isDate(value)) {\n      obj[key] = new Date(value)\n    }\n  }\n\n  return obj as T\n}\n\n// D1 doesnt like undefined, it wants null when calling bind\nfunction cleanBindings(bindings: any[]) {\n  return bindings.map((e) => (e === undefined ? null : e))\n}\n\nexport async function createRecord<RecordType>(\n  db: D1Database,\n  CREATE_SQL: string,\n  bindings: any[],\n  GET_SQL: string,\n  getBindings: any[]\n) {\n  try {\n    bindings = cleanBindings(bindings)\n    await db\n      .prepare(CREATE_SQL)\n      .bind(...bindings)\n      .run()\n    return await getRecord<RecordType>(db, GET_SQL, getBindings)\n  } catch (e: any) {\n    console.error(e.message, e.cause?.message)\n    throw e\n  }\n}\n\nexport async function getRecord<RecordType>(\n  db: D1Database,\n  SQL: string,\n  bindings: any[]\n): Promise<RecordType | null> {\n  try {\n    bindings = cleanBindings(bindings)\n    const res: any = await db\n      .prepare(SQL)\n      .bind(...bindings)\n      .first()\n    if (res) {\n      return format<RecordType>(res)\n    } else {\n      return null\n    }\n  } catch (e: any) {\n    console.error(e.message, e.cause?.message)\n    throw e\n  }\n}\n\nexport async function updateRecord(\n  db: D1Database,\n  SQL: string,\n  bindings: any[]\n) {\n  try {\n    bindings = cleanBindings(bindings)\n    return await db\n      .prepare(SQL)\n      .bind(...bindings)\n      .run()\n  } catch (e: any) {\n    console.error(e.message, e.cause?.message)\n    throw e\n  }\n}\n\nexport async function deleteRecord(\n  db: D1Database,\n  SQL: string,\n  bindings: any[]\n) {\n  try {\n    bindings = cleanBindings(bindings)\n    await db\n      .prepare(SQL)\n      .bind(...bindings)\n      .run()\n  } catch (e: any) {\n    console.error(e.message, e.cause?.message)\n    throw e\n  }\n}\n\nexport function D1Adapter(db: D1Database): Adapter {\n  // we need to run migrations if we dont have the right tables\n\n  return {\n    async createUser(user) {\n      const id: string = crypto.randomUUID()\n      const createBindings = [\n        id,\n        user.name,\n        user.email,\n        user.emailVerified?.toISOString(),\n        user.image,\n      ]\n      const getBindings = [id]\n\n      const newUser = await createRecord<AdapterUser>(\n        db,\n        CREATE_USER_SQL,\n        createBindings,\n        GET_USER_BY_ID_SQL,\n        getBindings\n      )\n      if (newUser) return newUser\n      throw new Error(\"Error creating user: Cannot get user after creation.\")\n    },\n    async getUser(id) {\n      return await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [id])\n    },\n    async getUserByEmail(email) {\n      return await getRecord<AdapterUser>(db, GET_USER_BY_EMAIL_SQL, [email])\n    },\n    async getUserByAccount({ providerAccountId, provider }) {\n      return await getRecord<AdapterUser>(db, GET_USER_BY_ACCOUNTL_SQL, [\n        providerAccountId,\n        provider,\n      ])\n    },\n    async updateUser(user) {\n      const params = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [\n        user.id,\n      ])\n      if (params) {\n        // copy any properties not in the update into the existing one and use that for bind params\n        // covers the scenario where the user arg doesnt have all of the current users properties\n        Object.assign(params, user)\n        const res = await updateRecord(db, UPDATE_USER_BY_ID_SQL, [\n          params.name,\n          params.email,\n          params.emailVerified?.toISOString(),\n          params.image,\n          params.id,\n        ])\n        if (res.success) {\n          const user = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [\n            params.id,\n          ])\n          if (user) return user\n          throw new Error(\n            \"Error updating user: Cannot get user after updating.\"\n          )\n        }\n      }\n      throw new Error(\"Error updating user: Failed to run the update SQL.\")\n    },\n    async deleteUser(userId) {\n      // miniflare doesn't support batch operations or multiline sql statements\n      await deleteRecord(db, DELETE_ACCOUNT_BY_USER_ID_SQL, [userId])\n      await deleteRecord(db, DELETE_SESSION_BY_USER_ID_SQL, [userId])\n      await deleteRecord(db, DELETE_USER_SQL, [userId])\n      return null\n    },\n    async linkAccount(a) {\n      // convert user_id to userId and provider_account_id to providerAccountId\n      const id = crypto.randomUUID()\n      const createBindings = [\n        id,\n        a.userId,\n        a.type,\n        a.provider,\n        a.providerAccountId,\n        a.refresh_token,\n        a.access_token,\n        a.expires_at,\n        a.token_type,\n        a.scope,\n        a.id_token,\n        a.session_state,\n        a.oauth_token ?? null,\n        a.oauth_token_secret ?? null,\n      ]\n      const getBindings = [id]\n      return await createRecord<AdapterAccount>(\n        db,\n        CREATE_ACCOUNT_SQL,\n        createBindings,\n        GET_ACCOUNT_BY_ID_SQL,\n        getBindings\n      )\n    },\n    async unlinkAccount({ providerAccountId, provider }) {\n      await deleteRecord(\n        db,\n        DELETE_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,\n        [provider, providerAccountId]\n      )\n    },\n    async createSession({ sessionToken, userId, expires }) {\n      const id = crypto.randomUUID()\n      const createBindings = [id, sessionToken, userId, expires.toISOString()]\n      const getBindings = [sessionToken]\n      const session = await createRecord<AdapterSession>(\n        db,\n        CREATE_SESSION_SQL,\n        createBindings,\n        GET_SESSION_BY_TOKEN_SQL,\n        getBindings\n      )\n      if (session) return session\n      throw new Error(`Couldn't create session`)\n    },\n    async getSessionAndUser(sessionToken) {\n      const session: any = await getRecord<AdapterSession>(\n        db,\n        GET_SESSION_BY_TOKEN_SQL,\n        [sessionToken]\n      )\n      if (session === null) return null\n\n      const user = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [\n        session.userId,\n      ])\n      if (user === null) return null\n\n      return { session, user }\n    },\n    async updateSession({ sessionToken, expires }) {\n      if (expires === undefined) {\n        await deleteRecord(db, DELETE_SESSION_SQL, [sessionToken])\n        return null\n      }\n      const session = await getRecord<AdapterSession>(\n        db,\n        GET_SESSION_BY_TOKEN_SQL,\n        [sessionToken]\n      )\n      if (!session) return null\n      session.expires = expires\n      await updateRecord(db, UPDATE_SESSION_BY_SESSION_TOKEN_SQL, [\n        expires?.toISOString(),\n        sessionToken,\n      ])\n      return await db\n        .prepare(UPDATE_SESSION_BY_SESSION_TOKEN_SQL)\n        .bind(expires?.toISOString(), sessionToken)\n        .first()\n    },\n    async deleteSession(sessionToken) {\n      await deleteRecord(db, DELETE_SESSION_SQL, [sessionToken])\n      return null\n    },\n    async createVerificationToken({ identifier, expires, token }) {\n      return await createRecord(\n        db,\n        CREATE_VERIFICATION_TOKEN_SQL,\n        [identifier, expires.toISOString(), token],\n        GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,\n        [identifier, token]\n      )\n    },\n    async useVerificationToken({ identifier, token }) {\n      const verificationToken = await getRecord<AdapterVerificationToken>(\n        db,\n        GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,\n        [identifier, token]\n      )\n      if (!verificationToken) return null\n      await deleteRecord(db, DELETE_VERIFICATION_TOKEN_SQL, [identifier, token])\n      return verificationToken\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-d1/src/migrations.ts",
    "content": "import type { D1Database } from \"./index.js\"\n\nexport const upSQLStatements = [\n  `CREATE TABLE IF NOT EXISTS \"accounts\" (\n    \"id\" text NOT NULL,\n    \"userId\" text NOT NULL DEFAULT NULL,\n    \"type\" text NOT NULL DEFAULT NULL,\n    \"provider\" text NOT NULL DEFAULT NULL,\n    \"providerAccountId\" text NOT NULL DEFAULT NULL,\n    \"refresh_token\" text DEFAULT NULL,\n    \"access_token\" text DEFAULT NULL,\n    \"expires_at\" number DEFAULT NULL,\n    \"token_type\" text DEFAULT NULL,\n    \"scope\" text DEFAULT NULL,\n    \"id_token\" text DEFAULT NULL,\n    \"session_state\" text DEFAULT NULL,\n    \"oauth_token_secret\" text DEFAULT NULL,\n    \"oauth_token\" text DEFAULT NULL,\n    PRIMARY KEY (id)\n);`,\n  `CREATE TABLE IF NOT EXISTS \"sessions\" (\n    \"id\" text NOT NULL,\n    \"sessionToken\" text NOT NULL,\n    \"userId\" text NOT NULL DEFAULT NULL,\n    \"expires\" datetime NOT NULL DEFAULT NULL, \n    PRIMARY KEY (sessionToken)\n);`,\n  `CREATE TABLE IF NOT EXISTS \"users\" (\n    \"id\" text NOT NULL DEFAULT '',\n    \"name\" text DEFAULT NULL,\n    \"email\" text DEFAULT NULL,\n    \"emailVerified\" datetime DEFAULT NULL,\n    \"image\" text DEFAULT NULL, \n    PRIMARY KEY (id)\n);`,\n  `CREATE TABLE IF NOT EXISTS \"verification_tokens\" (\n    \"identifier\" text NOT NULL,\n    \"token\" text NOT NULL DEFAULT NULL,\n    \"expires\" datetime NOT NULL DEFAULT NULL, \n    PRIMARY KEY (token)\n);`,\n]\n\nexport const down = [\n  `DROP TABLE IF EXISTS \"accounts\";`,\n  `DROP TABLE IF EXISTS \"sessions\";`,\n  `DROP TABLE IF EXISTS \"users\";`,\n  `DROP TABLE IF EXISTS \"verification_tokens\";`,\n]\n\nasync function up(db: D1Database) {\n  upSQLStatements.forEach(async (sql) => {\n    try {\n      await db.prepare(sql).run()\n    } catch (e: any) {\n      console.error(e.cause?.message, e.message)\n    }\n  })\n}\n\nexport { up }\n"
  },
  {
    "path": "packages/adapter-d1/src/queries.ts",
    "content": "// USER\nexport const CREATE_USER_SQL = `INSERT INTO users (id, name, email, emailVerified, image) VALUES (?, ?, ?, ?, ?)`\nexport const GET_USER_BY_ID_SQL = `SELECT * FROM users WHERE id = ?`\nexport const GET_USER_BY_EMAIL_SQL = `SELECT * FROM users WHERE email = ?`\nexport const GET_USER_BY_ACCOUNTL_SQL = `\n  SELECT u.*\n  FROM users u JOIN accounts a ON a.userId = u.id\n  WHERE a.providerAccountId = ? AND a.provider = ?`\nexport const UPDATE_USER_BY_ID_SQL = `\n  UPDATE users \n  SET name = ?, email = ?, emailVerified = ?, image = ?\n  WHERE id = ? `\nexport const DELETE_USER_SQL = `DELETE FROM users WHERE id = ?`\n\n// SESSION\nexport const CREATE_SESSION_SQL =\n  \"INSERT INTO sessions (id, sessionToken, userId, expires) VALUES (?,?,?,?)\"\nexport const GET_SESSION_BY_TOKEN_SQL = `\n  SELECT id, sessionToken, userId, expires\n  FROM sessions\n  WHERE sessionToken = ?`\nexport const UPDATE_SESSION_BY_SESSION_TOKEN_SQL = `UPDATE sessions SET expires = ? WHERE sessionToken = ?`\nexport const DELETE_SESSION_SQL = `DELETE FROM sessions WHERE sessionToken = ?`\nexport const DELETE_SESSION_BY_USER_ID_SQL = `DELETE FROM sessions WHERE userId = ?`\n\n// ACCOUNT\nexport const CREATE_ACCOUNT_SQL = `\n  INSERT INTO accounts (\n    id, userId, type, provider, \n    providerAccountId, refresh_token, access_token, \n    expires_at, token_type, scope, id_token, session_state,\n    oauth_token, oauth_token_secret\n  ) \n  VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)`\nexport const GET_ACCOUNT_BY_ID_SQL = `SELECT * FROM accounts WHERE id = ? `\nexport const GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL = `SELECT * FROM accounts WHERE provider = ? AND providerAccountId = ?`\nexport const DELETE_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL = `DELETE FROM accounts WHERE provider = ? AND providerAccountId = ?`\nexport const DELETE_ACCOUNT_BY_USER_ID_SQL = `DELETE FROM accounts WHERE userId = ?`\n\n// VERIFICATION_TOKEN\nexport const GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL = `SELECT * FROM verification_tokens WHERE identifier = ? AND token = ?`\nexport const CREATE_VERIFICATION_TOKEN_SQL = `INSERT INTO verification_tokens (identifier, expires, token) VALUES (?,?,?)`\nexport const DELETE_VERIFICATION_TOKEN_SQL = `DELETE FROM verification_tokens WHERE identifier = ? and token = ?`\n"
  },
  {
    "path": "packages/adapter-d1/test/index.test.ts",
    "content": "import { beforeAll } from \"vitest\"\n\nimport { D1Adapter, up, getRecord } from \"../src/\"\nimport {\n  GET_USER_BY_ID_SQL,\n  GET_SESSION_BY_TOKEN_SQL,\n  GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,\n  GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,\n} from \"../src/queries\"\nimport {\n  AdapterSession,\n  AdapterUser,\n  AdapterAccount,\n} from \"@auth/core/adapters\"\nimport { D1Database, D1DatabaseAPI } from \"@miniflare/d1\"\nimport { runBasicTests } from \"utils/adapter\"\nimport Database from \"better-sqlite3\"\n\nconst sqliteDB = new Database(\":memory:\")\nlet db = new D1Database(new D1DatabaseAPI(sqliteDB as any))\nlet adapter = D1Adapter(db)\n\nbeforeAll(async () => await up(db))\nrunBasicTests({\n  adapter,\n  db: {\n    user: async (id) =>\n      await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [id]),\n    session: async (sessionToken) =>\n      await getRecord<AdapterSession>(db, GET_SESSION_BY_TOKEN_SQL, [\n        sessionToken,\n      ]),\n    account: async ({ provider, providerAccountId }) =>\n      await getRecord<AdapterAccount>(\n        db,\n        GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,\n        [provider, providerAccountId]\n      ),\n    verificationToken: async ({ identifier, token }) =>\n      await getRecord(db, GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL, [\n        identifier,\n        token,\n      ]),\n  },\n})\n"
  },
  {
    "path": "packages/adapter-d1/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-d1/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/d1-adapter\",\n  entryFileName: \"../d1-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-dgraph/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://dgraph.io\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/dgraph.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Dgraph Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/dgraph-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/dgraph-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/dgraph-adapter?color=green&label=@auth/dgraph-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/dgraph-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/dgraph-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/dgraph).\n"
  },
  {
    "path": "packages/adapter-dgraph/package.json",
    "content": "{\n  \"name\": \"@auth/dgraph-adapter\",\n  \"version\": \"2.11.0\",\n  \"description\": \"Dgraph adapter for Auth.js\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Arnaud Derbey <arnaud@derbey.dev>\",\n  \"contributors\": [\n    \"Balázs Orbán <info@balazsorban.com>\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"lib\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"dgraph\",\n    \"graphql\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"tsc\",\n    \"test\": \"./test/test.sh\",\n    \"clean\": \"rm -rf *.js *.d.ts* lib\"\n  },\n  \"devDependencies\": {\n    \"@types/jsonwebtoken\": \"^8.5.5\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\",\n    \"jsonwebtoken\": \"^9.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-dgraph/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: \"16px\"}}>\n *  <p style={{fontWeight: \"normal\"}}>Official <a href=\"https://dgraph.io/docs\">Dgraph</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://dgraph.io/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/dgraph.svg\" width=\"100\"/>\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install next-auth @auth/dgraph-adapter\n * ```\n *\n * @module @auth/dgraph-adapter\n */\nimport { client as dgraphClient } from \"./lib/client.js\"\nimport { isDate, type Adapter } from \"@auth/core/adapters\"\nimport type { DgraphClientParams } from \"./lib/client.js\"\nimport * as defaultFragments from \"./lib/graphql/fragments.js\"\nimport {\n  AdapterAccount,\n  AdapterSession,\n  AdapterUser,\n  VerificationToken,\n} from \"@auth/core/adapters\"\n\nexport type { DgraphClientParams, DgraphClientError } from \"./lib/client.js\"\n\n/** This is the interface of the Dgraph adapter options. */\nexport interface DgraphAdapterOptions {\n  /**\n   * The GraphQL {@link https://dgraph.io/docs/query-language/fragments/ Fragments} you can supply to the adapter\n   * to define how the shapes of the `user`, `account`, `session`, `verificationToken` entities look.\n   *\n   * By default the adapter will uses the [default defined fragments](https://github.com/nextauthjs/next-auth/blob/main/packages/adapter-dgraph/src/lib/graphql/fragments.ts)\n   * , this config option allows to extend them.\n   */\n  fragments?: {\n    User?: string\n    Account?: string\n    Session?: string\n    VerificationToken?: string\n  }\n}\n\nexport function DgraphAdapter(\n  client: DgraphClientParams,\n  options?: DgraphAdapterOptions\n): Adapter {\n  const c = dgraphClient(client)\n\n  const fragments = { ...defaultFragments, ...options?.fragments }\n  return {\n    async createUser(input: AdapterUser) {\n      const result = await c.run<{ user: any[] }>(\n        /* GraphQL */ `\n          mutation ($input: [AddUserInput!]!) {\n            addUser(input: $input) {\n              user {\n                ...UserFragment\n              }\n            }\n          }\n          ${fragments.User}\n        `,\n        { input }\n      )\n\n      return format.from<any>(result?.user[0])\n    },\n    async getUser(id: string) {\n      const result = await c.run<any>(\n        /* GraphQL */ `\n          query ($id: ID!) {\n            getUser(id: $id) {\n              ...UserFragment\n            }\n          }\n          ${fragments.User}\n        `,\n        { id }\n      )\n\n      return format.from<any>(result)\n    },\n    async getUserByEmail(email: string) {\n      const [user] = await c.run<any>(\n        /* GraphQL */ `\n          query ($email: String = \"\") {\n            queryUser(filter: { email: { eq: $email } }) {\n              ...UserFragment\n            }\n          }\n          ${fragments.User}\n        `,\n        { email }\n      )\n      return format.from<any>(user)\n    },\n    async getUserByAccount(provider_providerAccountId: {\n      provider: string\n      providerAccountId: string\n    }) {\n      const [account] = await c.run<any>(\n        /* GraphQL */ `\n          query ($providerAccountId: String = \"\", $provider: String = \"\") {\n            queryAccount(\n              filter: {\n                and: {\n                  providerAccountId: { eq: $providerAccountId }\n                  provider: { eq: $provider }\n                }\n              }\n            ) {\n              user {\n                ...UserFragment\n              }\n              id\n            }\n          }\n          ${fragments.User}\n        `,\n        provider_providerAccountId\n      )\n      return format.from<any>(account?.user)\n    },\n    async updateUser({ id, ...input }: { id: string }) {\n      const result = await c.run<any>(\n        /* GraphQL */ `\n          mutation ($id: [ID!] = \"\", $input: UserPatch) {\n            updateUser(input: { filter: { id: $id }, set: $input }) {\n              user {\n                ...UserFragment\n              }\n            }\n          }\n          ${fragments.User}\n        `,\n        { id, input }\n      )\n      return format.from<any>(result.user[0])\n    },\n    async deleteUser(id: string) {\n      const result = await c.run<any>(\n        /* GraphQL */ `\n          mutation ($id: [ID!] = \"\") {\n            deleteUser(filter: { id: $id }) {\n              numUids\n              user {\n                accounts {\n                  id\n                }\n                sessions {\n                  id\n                }\n              }\n            }\n          }\n        `,\n        { id }\n      )\n\n      const deletedUser = format.from<any>(result.user[0])\n\n      await c.run<any>(\n        /* GraphQL */ `\n          mutation ($accounts: [ID!], $sessions: [ID!]) {\n            deleteAccount(filter: { id: $accounts }) {\n              numUids\n            }\n            deleteSession(filter: { id: $sessions }) {\n              numUids\n            }\n          }\n        `,\n        {\n          sessions: deletedUser.sessions.map((x: any) => x.id),\n          accounts: deletedUser.accounts.map((x: any) => x.id),\n        }\n      )\n\n      return deletedUser\n    },\n\n    async linkAccount(data: AdapterAccount) {\n      const { userId, ...input } = data\n      await c.run<any>(\n        /* GraphQL */ `\n          mutation ($input: [AddAccountInput!]!) {\n            addAccount(input: $input) {\n              account {\n                ...AccountFragment\n              }\n            }\n          }\n          ${fragments.Account}\n        `,\n        { input: { ...input, user: { id: userId } } }\n      )\n      return data\n    },\n    async unlinkAccount(provider_providerAccountId: {\n      provider: string\n      providerAccountId: string\n    }) {\n      await c.run<any>(\n        /* GraphQL */ `\n          mutation ($providerAccountId: String = \"\", $provider: String = \"\") {\n            deleteAccount(\n              filter: {\n                and: {\n                  providerAccountId: { eq: $providerAccountId }\n                  provider: { eq: $provider }\n                }\n              }\n            ) {\n              numUids\n            }\n          }\n        `,\n        provider_providerAccountId\n      )\n    },\n\n    async getSessionAndUser(sessionToken: string) {\n      const [sessionAndUser] = await c.run<any>(\n        /* GraphQL */ `\n          query ($sessionToken: String = \"\") {\n            querySession(filter: { sessionToken: { eq: $sessionToken } }) {\n              ...SessionFragment\n              user {\n                ...UserFragment\n              }\n            }\n          }\n          ${fragments.User}\n          ${fragments.Session}\n        `,\n        { sessionToken }\n      )\n      if (!sessionAndUser) return null\n\n      const { user, ...session } = sessionAndUser\n\n      return {\n        user: format.from<any>(user),\n        session: { ...format.from<any>(session), userId: user.id },\n      }\n    },\n    async createSession(data: AdapterSession) {\n      const { userId, ...input } = data\n\n      await c.run<any>(\n        /* GraphQL */ `\n          mutation ($input: [AddSessionInput!]!) {\n            addSession(input: $input) {\n              session {\n                ...SessionFragment\n              }\n            }\n          }\n          ${fragments.Session}\n        `,\n        { input: { ...input, user: { id: userId } } }\n      )\n\n      return data as any\n    },\n    async updateSession({ sessionToken, ...input }: { sessionToken: string }) {\n      const result = await c.run<any>(\n        /* GraphQL */ `\n          mutation ($input: SessionPatch = {}, $sessionToken: String) {\n            updateSession(\n              input: {\n                filter: { sessionToken: { eq: $sessionToken } }\n                set: $input\n              }\n            ) {\n              session {\n                ...SessionFragment\n                user {\n                  id\n                }\n              }\n            }\n          }\n          ${fragments.Session}\n        `,\n        { sessionToken, input }\n      )\n      const session = format.from<any>(result.session[0])\n\n      if (!session?.user?.id) return null\n\n      return { ...session, userId: session.user.id }\n    },\n    async deleteSession(sessionToken: string) {\n      await c.run<any>(\n        /* GraphQL */ `\n          mutation ($sessionToken: String = \"\") {\n            deleteSession(filter: { sessionToken: { eq: $sessionToken } }) {\n              numUids\n            }\n          }\n        `,\n        { sessionToken }\n      )\n    },\n\n    async createVerificationToken(input: VerificationToken) {\n      const result = await c.run<any>(\n        /* GraphQL */ `\n          mutation ($input: [AddVerificationTokenInput!]!) {\n            addVerificationToken(input: $input) {\n              numUids\n            }\n          }\n        `,\n        { input }\n      )\n      return format.from<any>(result)\n    },\n\n    async useVerificationToken(params: { identifier: string; token: string }) {\n      const result = await c.run<any>(\n        /* GraphQL */ `\n          mutation ($token: String = \"\", $identifier: String = \"\") {\n            deleteVerificationToken(\n              filter: {\n                and: { token: { eq: $token }, identifier: { eq: $identifier } }\n              }\n            ) {\n              verificationToken {\n                ...VerificationTokenFragment\n              }\n            }\n          }\n          ${fragments.VerificationToken}\n        `,\n        params\n      )\n\n      return format.from<any>(result.verificationToken[0])\n    },\n  }\n}\n\nexport const format = {\n  from<T>(object?: Record<string, any>): T | null {\n    const newObject: Record<string, unknown> = {}\n    if (!object) return null\n    for (const key in object) {\n      const value = object[key]\n      if (isDate(value)) {\n        newObject[key] = new Date(value)\n      } else {\n        newObject[key] = value\n      }\n    }\n\n    return newObject as T\n  },\n}\n"
  },
  {
    "path": "packages/adapter-dgraph/src/lib/client.ts",
    "content": "import * as jwt from \"jsonwebtoken\"\n\nexport interface DgraphClientParams {\n  endpoint: string\n  /**\n   * `X-Auth-Token` header value\n   *\n   * [Dgraph Cloud Authentication](https://dgraph.io/docs/cloud/cloud-api/overview/#dgraph-cloud-authentication)\n   */\n  authToken: string\n  /** [Using JWT and authorization claims](https://dgraph.io/docs/graphql/authorization/authorization-overview#using-jwts-and-authorization-claims) */\n  jwtSecret?: string\n  /**\n   * @default \"RS256\"\n   *\n   * [Using JWT and authorization claims](https://dgraph.io/docs/graphql/authorization/authorization-overview#using-jwts-and-authorization-claims)\n   */\n  jwtAlgorithm?: \"HS256\" | \"RS256\"\n  /**\n   * @default \"Authorization\"\n   *\n   * [Using JWT and authorization claims](https://dgraph.io/docs/graphql/authorization/authorization-overview#using-jwts-and-authorization-claims)\n   */\n  authHeader?: string\n}\n\nexport class DgraphClientError extends Error {\n  name = \"DgraphClientError\"\n  constructor(errors: any[], query: string, variables: any) {\n    super(errors.map((error) => error.message).join(\"\\n\"))\n    console.error({ query, variables })\n  }\n}\n\nexport function client(params: DgraphClientParams) {\n  if (!params.authToken) {\n    throw new Error(\"Dgraph client error: Please provide an API key\")\n  }\n  if (!params.endpoint) {\n    throw new Error(\n      \"Dgraph client error: Please provide a valid GraphQL endpoint\"\n    )\n  }\n\n  const {\n    endpoint,\n    authToken,\n    jwtSecret,\n    jwtAlgorithm = \"HS256\",\n    authHeader = \"Authorization\",\n  } = params\n  const headers: HeadersInit = {\n    \"Content-Type\": \"application/json\",\n    \"X-Auth-Token\": authToken,\n  }\n\n  if (authHeader && jwtSecret) {\n    headers[authHeader] = jwt.sign({ nextAuth: true }, jwtSecret, {\n      algorithm: jwtAlgorithm,\n    })\n  }\n\n  return {\n    async run<T>(\n      query: string,\n      variables?: Record<string, any>\n    ): Promise<T | null> {\n      const response = await fetch(endpoint, {\n        method: \"POST\",\n        headers,\n        body: JSON.stringify({ query, variables }),\n      })\n\n      const { data = {}, errors } = await response.json()\n      if (errors?.length) {\n        throw new DgraphClientError(errors, query, variables)\n      }\n      return Object.values(data)[0] as any\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-dgraph/src/lib/graphql/fragments.ts",
    "content": "export const User = /* GraphQL */ `\n  fragment UserFragment on User {\n    email\n    id\n    image\n    name\n    emailVerified\n  }\n`\n\nexport const Account = /* GraphQL */ `\n  fragment AccountFragment on Account {\n    id\n    type\n    provider\n    providerAccountId\n    expires_at\n    token_type\n    scope\n    access_token\n    refresh_token\n    id_token\n    session_state\n  }\n`\nexport const Session = /* GraphQL */ `\n  fragment SessionFragment on Session {\n    expires\n    id\n    sessionToken\n  }\n`\n\nexport const VerificationToken = /* GraphQL */ `\n  fragment VerificationTokenFragment on VerificationToken {\n    identifier\n    token\n    expires\n  }\n`\n"
  },
  {
    "path": "packages/adapter-dgraph/src/lib/graphql/schema.gql",
    "content": "type Account\n  @auth(\n    delete: { rule: \"{$nextAuth: { eq: true } }\" }\n    add: { rule: \"{$nextAuth: { eq: true } }\" }\n    query: { rule: \"{$nextAuth: { eq: true } }\" }\n    update: { rule: \"{$nextAuth: { eq: true } }\" }\n  ) {\n  id: ID\n  type: String\n  provider: String @search(by: [hash])\n  providerAccountId: String @search(by: [hash])\n  refreshToken: String\n  expires_at: Int64\n  accessToken: String\n  token_type: String\n  refresh_token: String\n  access_token: String\n  scope: String\n  id_token: String\n  session_state: String\n  user: User @hasInverse(field: \"accounts\")\n}\ntype Session\n  @auth(\n    delete: { rule: \"{$nextAuth: { eq: true } }\" }\n    add: { rule: \"{$nextAuth: { eq: true } }\" }\n    query: { rule: \"{$nextAuth: { eq: true } }\" }\n    update: { rule: \"{$nextAuth: { eq: true } }\" }\n  ) {\n  id: ID\n  expires: DateTime\n  sessionToken: String @search(by: [hash])\n  user: User @hasInverse(field: \"sessions\")\n}\ntype User\n  @auth(\n    query: {\n      or: [\n        {\n          rule: \"\"\"\n          query ($userId: ID!) {queryUser(filter: { id: [$userId] } ) {id}}\n          \"\"\"\n        }\n        { rule: \"{$nextAuth: { eq: true } }\" }\n      ]\n    }\n    delete: { rule: \"{$nextAuth: { eq: true } }\" }\n    add: { rule: \"{$nextAuth: { eq: true } }\" }\n    update: {\n      or: [\n        {\n          rule: \"\"\"\n          query ($userId: ID!) {queryUser(filter: { id: [$userId] } ) {id}}\n          \"\"\"\n        }\n        { rule: \"{$nextAuth: { eq: true } }\" }\n      ]\n    }\n  ) {\n  id: ID\n  name: String\n  email: String @search(by: [hash])\n  emailVerified: DateTime\n  image: String\n  accounts: [Account] @hasInverse(field: \"user\")\n  sessions: [Session] @hasInverse(field: \"user\")\n}\n\ntype VerificationToken\n  @auth(\n    delete: { rule: \"{$nextAuth: { eq: true } }\" }\n    add: { rule: \"{$nextAuth: { eq: true } }\" }\n    query: { rule: \"{$nextAuth: { eq: true } }\" }\n    update: { rule: \"{$nextAuth: { eq: true } }\" }\n  ) {\n  id: ID\n  identifier: String @search(by: [hash])\n  token: String @search(by: [hash])\n  expires: DateTime\n}\n\n# Dgraph.Authorization {\"VerificationKey\":\"<YOUR JWT SECRET HERE>\",\"Header\":\"<YOUR AUTH HEADER HERE>\",\"Namespace\":\"<YOUR CUSTOM NAMESPACE HERE>\",\"Algo\":\"RS256\"}\n"
  },
  {
    "path": "packages/adapter-dgraph/test/index.test.ts",
    "content": "import { DgraphAdapter, format } from \"../src\"\nimport { client as dgraphClient } from \"../src/lib/client\"\nimport * as fragments from \"../src/lib/graphql/fragments\"\nimport { runBasicTests } from \"utils/adapter\"\nimport fs from \"fs\"\nimport path from \"path\"\n\nimport type { DgraphClientParams } from \"../src\"\n\nconst params: DgraphClientParams = {\n  endpoint: \"http://localhost:8080/graphql\",\n  authToken: \"test\",\n  jwtAlgorithm: \"RS256\",\n  jwtSecret: fs.readFileSync(path.join(process.cwd(), \"/test/private.key\"), {\n    encoding: \"utf8\",\n  }),\n}\n\n/** TODO: Add test to `dgraphClient` */\nconst c = dgraphClient(params)\n\nrunBasicTests({\n  adapter: DgraphAdapter(params),\n  db: {\n    id: () => \"0x0a0a00a00\",\n    async disconnect() {\n      await c.run(/* GraphQL */ `\n        mutation {\n          deleteUser(filter: {}) {\n            numUids\n          }\n          deleteVerificationToken(filter: {}) {\n            numUids\n          }\n          deleteSession(filter: {}) {\n            numUids\n          }\n          deleteAccount(filter: {}) {\n            numUids\n          }\n        }\n      `)\n    },\n    async user(id) {\n      const result = await c.run<any>(\n        /* GraphQL */ `\n          query ($id: ID!) {\n            getUser(id: $id) {\n              ...UserFragment\n            }\n          }\n          ${fragments.User}\n        `,\n        { id }\n      )\n\n      return format.from(result)\n    },\n    async session(sessionToken) {\n      const result = await c.run<any>(\n        /* GraphQL */ `\n          query ($sessionToken: String!) {\n            querySession(filter: { sessionToken: { eq: $sessionToken } }) {\n              ...SessionFragment\n              user {\n                id\n              }\n            }\n          }\n          ${fragments.Session}\n        `,\n        { sessionToken }\n      )\n\n      const { user, ...session } = result?.[0] ?? {}\n      if (!user?.id) return null\n      return format.from({ ...session, userId: user.id })\n    },\n    async account(provider_providerAccountId) {\n      const result = await c.run<any>(\n        /* GraphQL */ `\n          query ($providerAccountId: String = \"\", $provider: String = \"\") {\n            queryAccount(\n              filter: {\n                providerAccountId: { eq: $providerAccountId }\n                provider: { eq: $provider }\n              }\n            ) {\n              ...AccountFragment\n              user {\n                id\n              }\n            }\n          }\n          ${fragments.Account}\n        `,\n        provider_providerAccountId\n      )\n\n      const account = format.from<any>(result?.[0])\n      if (!account?.user) return null\n\n      account.userId = account.user.id\n      delete account.user\n      return account\n    },\n    async verificationToken(identifier_token) {\n      const result = await c.run<any>(\n        /* GraphQL */ `\n          query ($identifier: String = \"\", $token: String = \"\") {\n            queryVerificationToken(\n              filter: { identifier: { eq: $identifier }, token: { eq: $token } }\n            ) {\n              ...VerificationTokenFragment\n            }\n          }\n          ${fragments.VerificationToken}\n        `,\n        identifier_token\n      )\n\n      return format.from(result?.[0])\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-dgraph/test/private.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKQIBAAKCAgEAxqyvd82VacXMBLUADZt+euSNUNJ276XgvH4HW4ms5iQZDgYI\nPKxyaZ+wk8EMYSB1dymJ3WQpm0JKHqgTW+z/edfYFQXkduHN/zoIpxMAMyZGsTBi\ndGo0xJSHTCDCdYCCBlG9R1ljjhf0l9ChBP7W7lSXaRU/XS/tMH1qYMpsUwDav4G/\nRDI3A4t29JRGqU4mnFa5o3XBCxU4ANCp1JaQevzAYox8EGPZ1YZGmhRgca51dBee\nd9QKqWjfXP4wboC1ppglm+kPgFUaCiXB8KyfIixhlvzZiO4RLvZw+cILt586vXGz\nNy49eVUTiIOoTZuG/79pCeBS8BCbB4l6y274y42hUN83gHxQ32Y++DI40jz5iGN8\n5Dj6yDDjKwvwqVhCx/kVJFrmyrTJz/E0cp38FeIi7D6e0eXj7G97K+wkNdc4oTs1\nDsDPzhO/7wxQOZIjvNp+DJAfxin5MbM+UKoopvJj3sUMHVrTteWxZg94mmLjg2Kn\nJYBuSn8kiFPYQ0F5MjE7df4tDDTGJ/VEFIG5EkQffaNYhW0Z5ORLvW1R1Yd1/ew3\nUWo+mZ7XAUGLF6clsWSQvzSrrNMYzCk5Fa0LwvMtQdEVLL3q7/KsEHD7N78EVlmE\nDlOtC21UidUqXnawCE1QIjAHqFsNNPR2j0lgOoEjrGdzrvUg6hNV9m6CbSECAwEA\nAQKCAgAjr8kk/+yiv0DSZ6DG0PN7J6qqpeNvUKB5uzmfG6/O9xT5C+RW4bL7fg+9\nuqN6ntX6vZ9iASfoF5QwxYgUrxGE1VyfChvrrsvN2KLNQAB9L5brJQHKX3lzBir3\nZbsIWDkC4ZPaSRg04eCxlGwX9Z6t2MwJuCNVndJBL4X4NOQYVML2O1wb59kx7c9E\nR44Zw0v0MS/PSMuQLhONMe4Pnav+K4BzM0DlwMnULPZpntdkFC5M2CFC7PetToUw\nswgIEV6PuiynQMnkB2VSBU486QT8onQ1Jt38VqcHhITumAh6x0NJ3C6Q7uFj9gA4\nOU32AsXREpTPjVfYf2MZi3xfJmPR+1JTqmnhWY7g/v3K5MpFO9HGmcETNpV4YXRv\nU18Bx+m5FsKp0tFASyS/6PJoDAJ/a6yQxVNc1nYL8AKTFqod/0pQz2w2yFGR2t1g\nUi+7HQrWRpdvp2vDJK2GJLs+thybtd73QwsKJ2LFHS91eQ1y1BsSI4z1Ph8/66xK\nuQVWfeQqQIhbM8m/pzOYNw90jRx9raKZ6QpdmLqoKj4WF3a/KvLc0TO678wzVoSM\nqBDH9FwmkebNHWEMR8rR5Fb1ZVHclSde6DqdPBTvcQzMk66ZGMHB746G68620iKs\nYJ6dFDBt3XBnhhOjPhCCH4XR8ZIGTgwxC9hry17/sUMEU5iS8QKCAQEA7WnbfI+h\noLnfw0M6uxIrvl1MMip1Zq/f2/q3HIpE6qLuPoy4fzjONNYm8QBwdJSVPviMCsFx\nrU2IIHLeQGUSvMIIcWzn+EWKl3XTzirdn9uYZPPqGr/YuoLW/uN2TCppBbzT1jtA\nbbQYUfvyF+ysU+F9amLSdDsqM3MwaFMNChcf3XLMz7QFgoWIDSejq4Uhy6y22KEi\nqg+VprX9OejzUQLb0I8ko9S3dKAHkhUZJ8kxowu5oqaaGpowBhko84zKpNrGbinG\nbA0+LTxAUKaHhioWWgXya976DQRBdTkp7wOWuD/jnL3wnIHDYF0TKYuidu98d+zH\nb/+EH/wPEK4DrwKCAQEA1jpwJm2CDkw41TNexLectOlAjVw9xMt+10hLZ2PYOuwd\nkThLYU9zqYIp9thj9/Ddqkx286qHaX92W2q0SZmhuXeNLmcG71QGJp/6uC+up0Hk\n7aFPoQ3uS7JQN5YwinUy/0vbTsxmko0Ie9y2gA0bWDV4Yu5zr/vYd/bLD55GPRD/\nWWGWkDlzlQqedQkjaCSRskm6nyFdTSsruw6RMdNiZK6jBR2aY0SsFmJmOwrTrPCS\nllg+zaUtqwgC4tLROx8R5rkJh8S+/KjRN46oXPphQLTJlNZu1uTjV5Ue/BqpHgor\nhJLgZwfA7YXJFfiSfjYFYTj9vm9Wx50zJSKiEZxALwKCAQEA6Czcy8y/GKqN7Kwj\nlGypwMoGyQyCsYCPoNZoGo4R5ZCfAyalCy2nYz6G6KswTqI77lAszBvvqramyGzt\ncvYlQ9lRXnNNy5tedM5y6y06fanIN/ndWHmDXqqzzKLvvn6/JDBMzjY1xNMZ8Zs9\nXy5CPOnIt7Ca9bYiiBw/G9cUamjA7dTl/L2locYqjgrU4dkZetCWI/Y5KyyAgn95\nfBeXVANCqoxCHcHaA0C5BqCBcEous6+0xB6/mAJvspcKWFu4lU2qPnO2K1csFhrV\nHsoswQUJxNIKCHoP+YjO5u+XVbohvGAmnNOXqcaxJdz/72Ix6LQ9+h3h0GKGeK0M\nopg62wKCAQEAnyRoXdOp8s8ixRbVRtOTwT0prBmi9UeqoWjeQx8D6bmvuUqVjOOF\n6517aRmVIgI32SPWlerPj0qV9RFOfwJ3Bp1OLvNwTmgf7Z+YlC0v1KZ51yGnUuBT\nbr43IyQaSTEJQmfqsh3b8PB+Je1vUa7q6ltGZE/5dvli9LNMY/zS9thiqNZ7EAbt\n2wE5d33jZKEN7uEglsglVIdGhD4tFFOQ23R0O/+iyi2gnTxZ73B6kRVh//fsJ76W\nL2DTLAcqUX4iQUCiWM6Kho0uZtQ+NFv31Sa4PS4SxubgEBcCHov7qAosC99EfqVe\n59Qj7oNq6AFfe7rnnQl+8OjRrruMpAJsFwKCAQBxq1apDQTav7QW9Sfe19POZas0\nb0XIETL3mEh25uCqPTmoaKo45opgw0Cn7zpuy/NntKlG/cnPYneQh91bViqid/Iv\n3M88vQJmS2e4abozqa7iNjd/XwmBcCgdR2yx51oJ9q9dfd2ejKfMDzm0uHs5U7ay\npOlUch5OT0s5utZC4FbeziZ8Th61DtmIHOxQpNYpPXogdkbGSaOhL6dezPOAwJnJ\nB2zjH7N1c+dz+5HheVbN3M08aN9DdyD1xsmd8eZVTAi1wcp51GY6cb7G0gE2SzOp\nUNtVbc17n82jJ5Qr4ggSRU1QWNBZT9KX4U2/nTe3U5C3+ni4p+opI9Q3vSYw\n-----END RSA PRIVATE KEY-----"
  },
  {
    "path": "packages/adapter-dgraph/test/public.key",
    "content": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxqyvd82VacXMBLUADZt+\neuSNUNJ276XgvH4HW4ms5iQZDgYIPKxyaZ+wk8EMYSB1dymJ3WQpm0JKHqgTW+z/\nedfYFQXkduHN/zoIpxMAMyZGsTBidGo0xJSHTCDCdYCCBlG9R1ljjhf0l9ChBP7W\n7lSXaRU/XS/tMH1qYMpsUwDav4G/RDI3A4t29JRGqU4mnFa5o3XBCxU4ANCp1JaQ\nevzAYox8EGPZ1YZGmhRgca51dBeed9QKqWjfXP4wboC1ppglm+kPgFUaCiXB8Kyf\nIixhlvzZiO4RLvZw+cILt586vXGzNy49eVUTiIOoTZuG/79pCeBS8BCbB4l6y274\ny42hUN83gHxQ32Y++DI40jz5iGN85Dj6yDDjKwvwqVhCx/kVJFrmyrTJz/E0cp38\nFeIi7D6e0eXj7G97K+wkNdc4oTs1DsDPzhO/7wxQOZIjvNp+DJAfxin5MbM+UKoo\npvJj3sUMHVrTteWxZg94mmLjg2KnJYBuSn8kiFPYQ0F5MjE7df4tDDTGJ/VEFIG5\nEkQffaNYhW0Z5ORLvW1R1Yd1/ew3UWo+mZ7XAUGLF6clsWSQvzSrrNMYzCk5Fa0L\nwvMtQdEVLL3q7/KsEHD7N78EVlmEDlOtC21UidUqXnawCE1QIjAHqFsNNPR2j0lg\nOoEjrGdzrvUg6hNV9m6CbSECAwEAAQ==\n-----END PUBLIC KEY-----"
  },
  {
    "path": "packages/adapter-dgraph/test/test.sh",
    "content": "#!/usr/bin/env bash\n\nCONTAINER_NAME=authjs-dgraph\n\n# Start db\ndocker run -d --rm \\\n  -p 8080:8080 \\\n  -p 9080:9080 \\\n  --name \"${CONTAINER_NAME}\" \\\n  dgraph/standalone:latest\n\necho \"Waiting 15s for db to start...\" && sleep 15\n\nhead -n -1 src/lib/graphql/schema.gql >test/test.schema.gql\nPUBLIC_KEY=$(sed 's/$/\\\\n/' test/public.key | tr -d '\\n')\necho \"# Dgraph.Authorization {\\\"VerificationKey\\\":\\\"$PUBLIC_KEY\\\",\\\"Namespace\\\":\\\"https://dgraph.io/jwt/claims\\\",\\\"Header\\\":\\\"Authorization\\\",\\\"Algo\\\":\\\"RS256\\\"}\" >>test/test.schema.gql\n\ncurl -X POST localhost:8080/admin/schema --data-binary '@test/test.schema.gql'\n\nprintf \"\\nWaiting 5s for schema to be uploaded...\" && sleep 5\n\n# Always stop container, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  docker stop \"${CONTAINER_NAME}\"\nelse\n  docker stop \"${CONTAINER_NAME}\" && exit 1\nfi\n\nrm test/test.schema.gql\n"
  },
  {
    "path": "packages/adapter-dgraph/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-dgraph/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\", \"src/lib/client.ts\", \"src/lib/utils.ts\", \"src/lib/graphql/fragments.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/dgraph-adapter\",\n  entryFileName: \"../dgraph-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-drizzle/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://github.com/drizzle-team/drizzle-orm\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/drizzle.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Drizzle ORM Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/drizzle-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/drizzle-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/drizzle-adapter?color=green&label=@auth/drizzle-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/drizzle-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/drizzle-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/drizzle).\n"
  },
  {
    "path": "packages/adapter-drizzle/package.json",
    "content": "{\n  \"name\": \"@auth/drizzle-adapter\",\n  \"version\": \"1.11.0\",\n  \"description\": \"Drizzle adapter for Auth.js.\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Anthony Shew\",\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.d.ts*\",\n    \"*.js\",\n    \"lib\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\",\n      \"require\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"@auth\",\n    \"Auth.js\",\n    \"next.js\",\n    \"oauth\",\n    \"drizzle\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"clean\": \"rm -rf *.js *.d.ts* lib\",\n    \"clean:drizzle\": \"find . -type d -name \\\".drizzle\\\" | xargs rm -rf\",\n    \"test\": \"pnpm test:mysql && pnpm test:sqlite && pnpm test:pg\",\n    \"test:mysql\": \"pnpm clean && ./test/mysql/test.sh\",\n    \"test:sqlite\": \"pnpm clean && ./test/sqlite/test.sh\",\n    \"test:pg\": \"pnpm clean && ./test/pg/test.sh\",\n    \"build\": \"tsc\",\n    \"dev\": \"drizzle-kit generate:mysql --schema=src/schema.ts --out=.drizzle && tsc -w\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"@types/uuid\": \"^8.3.3\",\n    \"drizzle-kit\": \"^0.23.0\",\n    \"drizzle-orm\": \"^0.32.0\",\n    \"libsql\": \"^0.3.18\",\n    \"mysql2\": \"^3.9.7\",\n    \"postgres\": \"^3.4.3\",\n    \"tsx\": \"^4.7.0\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-drizzle/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>Official <a href=\"https://orm.drizzle.team\">Drizzle ORM</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://orm.drizzle.team\">\n *   <img style={{display: \"block\"}} src=\"/img/adapters/drizzle.svg\" width=\"38\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install drizzle-orm @auth/drizzle-adapter\n * npm install drizzle-kit --save-dev\n * ```\n *\n * @module @auth/drizzle-adapter\n */\n\nimport { is } from \"drizzle-orm\"\nimport { MySqlDatabase } from \"drizzle-orm/mysql-core\"\nimport { PgDatabase } from \"drizzle-orm/pg-core\"\nimport { BaseSQLiteDatabase } from \"drizzle-orm/sqlite-core\"\nimport { DefaultMySqlSchema, MySqlDrizzleAdapter } from \"./lib/mysql.js\"\nimport { DefaultPostgresSchema, PostgresDrizzleAdapter } from \"./lib/pg.js\"\nimport { DefaultSQLiteSchema, SQLiteDrizzleAdapter } from \"./lib/sqlite.js\"\nimport { DefaultSchema, SqlFlavorOptions } from \"./lib/utils.js\"\n\nimport type { Adapter } from \"@auth/core/adapters\"\n\nexport function DrizzleAdapter<SqlFlavor extends SqlFlavorOptions>(\n  db: SqlFlavor,\n  schema?: DefaultSchema<SqlFlavor>\n): Adapter {\n  if (is(db, MySqlDatabase)) {\n    return MySqlDrizzleAdapter(db, schema as DefaultMySqlSchema)\n  } else if (is(db, PgDatabase)) {\n    return PostgresDrizzleAdapter(db, schema as DefaultPostgresSchema)\n  } else if (is(db, BaseSQLiteDatabase)) {\n    return SQLiteDrizzleAdapter(db, schema as DefaultSQLiteSchema)\n  }\n\n  throw new Error(\n    `Unsupported database type (${typeof db}) in Auth.js Drizzle adapter.`\n  )\n}\n"
  },
  {
    "path": "packages/adapter-drizzle/src/lib/mysql.ts",
    "content": "import { GeneratedColumnConfig, and, eq, getTableColumns } from \"drizzle-orm\"\nimport {\n  MySqlColumn,\n  MySqlDatabase,\n  boolean,\n  int,\n  mysqlTable,\n  primaryKey,\n  timestamp,\n  varchar,\n  PreparedQueryHKTBase,\n  MySqlTableWithColumns,\n  MySqlQueryResultHKT,\n} from \"drizzle-orm/mysql-core\"\n\nimport type {\n  Adapter,\n  AdapterAccount,\n  AdapterAccountType,\n  AdapterSession,\n  AdapterUser,\n  VerificationToken,\n  AdapterAuthenticator,\n} from \"@auth/core/adapters\"\nimport { Awaitable } from \"@auth/core/types\"\n\nexport function defineTables(\n  schema: Partial<DefaultMySqlSchema> = {}\n): Required<DefaultMySqlSchema> {\n  const usersTable =\n    schema.usersTable ??\n    (mysqlTable(\"user\", {\n      id: varchar(\"id\", { length: 255 })\n        .primaryKey()\n        .$defaultFn(() => crypto.randomUUID()),\n      name: varchar(\"name\", { length: 255 }),\n      email: varchar(\"email\", { length: 255 }).unique(),\n      emailVerified: timestamp(\"emailVerified\", { mode: \"date\", fsp: 3 }),\n      image: varchar(\"image\", { length: 255 }),\n    }) satisfies DefaultMySqlUsersTable)\n\n  const accountsTable =\n    schema.accountsTable ??\n    (mysqlTable(\n      \"account\",\n      {\n        userId: varchar(\"userId\", { length: 255 })\n          .notNull()\n          .references(() => usersTable.id, { onDelete: \"cascade\" }),\n        type: varchar(\"type\", { length: 255 })\n          .$type<AdapterAccountType>()\n          .notNull(),\n        provider: varchar(\"provider\", { length: 255 }).notNull(),\n        providerAccountId: varchar(\"providerAccountId\", {\n          length: 255,\n        }).notNull(),\n        refresh_token: varchar(\"refresh_token\", { length: 255 }),\n        access_token: varchar(\"access_token\", { length: 255 }),\n        expires_at: int(\"expires_at\"),\n        token_type: varchar(\"token_type\", { length: 255 }),\n        scope: varchar(\"scope\", { length: 255 }),\n        id_token: varchar(\"id_token\", { length: 2048 }),\n        session_state: varchar(\"session_state\", { length: 255 }),\n      },\n      (account) => ({\n        compositePk: primaryKey({\n          columns: [account.provider, account.providerAccountId],\n        }),\n      })\n    ) satisfies DefaultMySqlAccountsTable)\n\n  const sessionsTable =\n    schema.sessionsTable ??\n    (mysqlTable(\"session\", {\n      sessionToken: varchar(\"sessionToken\", { length: 255 }).primaryKey(),\n      userId: varchar(\"userId\", { length: 255 })\n        .notNull()\n        .references(() => usersTable.id, { onDelete: \"cascade\" }),\n      expires: timestamp(\"expires\", { mode: \"date\" }).notNull(),\n    }) satisfies DefaultMySqlSessionsTable)\n\n  const verificationTokensTable =\n    schema.verificationTokensTable ??\n    (mysqlTable(\n      \"verificationToken\",\n      {\n        identifier: varchar(\"identifier\", { length: 255 }).notNull(),\n        token: varchar(\"token\", { length: 255 }).notNull(),\n        expires: timestamp(\"expires\", { mode: \"date\" }).notNull(),\n      },\n      (verficationToken) => ({\n        compositePk: primaryKey({\n          columns: [verficationToken.identifier, verficationToken.token],\n        }),\n      })\n    ) satisfies DefaultMySqlVerificationTokenTable)\n\n  const authenticatorsTable =\n    schema.authenticatorsTable ??\n    (mysqlTable(\n      \"authenticator\",\n      {\n        credentialID: varchar(\"credentialID\", { length: 255 })\n          .notNull()\n          .unique(),\n        userId: varchar(\"userId\", { length: 255 })\n          .notNull()\n          .references(() => usersTable.id, { onDelete: \"cascade\" }),\n        providerAccountId: varchar(\"providerAccountId\", {\n          length: 255,\n        }).notNull(),\n        credentialPublicKey: varchar(\"credentialPublicKey\", {\n          length: 255,\n        }).notNull(),\n        counter: int(\"counter\").notNull(),\n        credentialDeviceType: varchar(\"credentialDeviceType\", {\n          length: 255,\n        }).notNull(),\n        credentialBackedUp: boolean(\"credentialBackedUp\").notNull(),\n        transports: varchar(\"transports\", { length: 255 }),\n      },\n      (authenticator) => ({\n        compositePk: primaryKey({\n          columns: [authenticator.userId, authenticator.credentialID],\n        }),\n      })\n    ) satisfies DefaultMySqlAuthenticatorTable)\n\n  return {\n    usersTable,\n    accountsTable,\n    sessionsTable,\n    verificationTokensTable,\n    authenticatorsTable,\n  }\n}\n\nexport function MySqlDrizzleAdapter(\n  client: MySqlDatabase<MySqlQueryResultHKT, PreparedQueryHKTBase, any>,\n  schema?: DefaultMySqlSchema\n): Adapter {\n  const {\n    usersTable,\n    accountsTable,\n    sessionsTable,\n    verificationTokensTable,\n    authenticatorsTable,\n  } = defineTables(schema)\n\n  return {\n    async createUser(data: AdapterUser) {\n      const { id, ...insertData } = data\n      const hasDefaultId = getTableColumns(usersTable)[\"id\"][\"defaultFn\"]\n\n      const [insertedUser] = (await client\n        .insert(usersTable)\n        .values(hasDefaultId ? insertData : { ...insertData, id })\n        .$returningId()) as [{ id: string }] | []\n\n      return client\n        .select()\n        .from(usersTable)\n        .where(eq(usersTable.id, insertedUser ? insertedUser.id : id))\n        .then((res) => res[0]) as Awaitable<AdapterUser>\n    },\n    async getUser(userId: string) {\n      return client\n        .select()\n        .from(usersTable)\n        .where(eq(usersTable.id, userId))\n        .then((res) =>\n          res.length > 0 ? res[0] : null\n        ) as Awaitable<AdapterUser | null>\n    },\n    async getUserByEmail(email: string) {\n      return client\n        .select()\n        .from(usersTable)\n        .where(eq(usersTable.email, email))\n        .then((res) =>\n          res.length > 0 ? res[0] : null\n        ) as Awaitable<AdapterUser | null>\n    },\n    async createSession(data: {\n      sessionToken: string\n      userId: string\n      expires: Date\n    }) {\n      await client.insert(sessionsTable).values(data)\n\n      return client\n        .select()\n        .from(sessionsTable)\n        .where(eq(sessionsTable.sessionToken, data.sessionToken))\n        .then((res) => res[0])\n    },\n    async getSessionAndUser(sessionToken: string) {\n      return client\n        .select({\n          session: sessionsTable,\n          user: usersTable,\n        })\n        .from(sessionsTable)\n        .where(eq(sessionsTable.sessionToken, sessionToken))\n        .innerJoin(usersTable, eq(usersTable.id, sessionsTable.userId))\n        .then((res) => (res.length > 0 ? res[0] : null)) as Awaitable<{\n        session: AdapterSession\n        user: AdapterUser\n      } | null>\n    },\n    async updateUser(data: Partial<AdapterUser> & Pick<AdapterUser, \"id\">) {\n      if (!data.id) {\n        throw new Error(\"No user id.\")\n      }\n\n      await client\n        .update(usersTable)\n        .set(data)\n        .where(eq(usersTable.id, data.id))\n\n      const [result] = await client\n        .select()\n        .from(usersTable)\n        .where(eq(usersTable.id, data.id))\n\n      if (!result) {\n        throw new Error(\"No user found.\")\n      }\n\n      return result as Awaitable<AdapterUser>\n    },\n    async updateSession(\n      data: Partial<AdapterSession> & Pick<AdapterSession, \"sessionToken\">\n    ) {\n      await client\n        .update(sessionsTable)\n        .set(data)\n        .where(eq(sessionsTable.sessionToken, data.sessionToken))\n\n      return client\n        .select()\n        .from(sessionsTable)\n        .where(eq(sessionsTable.sessionToken, data.sessionToken))\n        .then((res) => res[0])\n    },\n    async linkAccount(data: AdapterAccount) {\n      await client.insert(accountsTable).values(data)\n    },\n    async getUserByAccount(\n      account: Pick<AdapterAccount, \"provider\" | \"providerAccountId\">\n    ) {\n      const result = await client\n        .select({\n          account: accountsTable,\n          user: usersTable,\n        })\n        .from(accountsTable)\n        .innerJoin(usersTable, eq(accountsTable.userId, usersTable.id))\n        .where(\n          and(\n            eq(accountsTable.provider, account.provider),\n            eq(accountsTable.providerAccountId, account.providerAccountId)\n          )\n        )\n        .then((res) => res[0])\n\n      const user = result?.user ?? null\n\n      return user as Awaitable<AdapterUser | null>\n    },\n    async deleteSession(sessionToken: string) {\n      await client\n        .delete(sessionsTable)\n        .where(eq(sessionsTable.sessionToken, sessionToken))\n    },\n    async createVerificationToken(data: VerificationToken) {\n      await client.insert(verificationTokensTable).values(data)\n\n      return client\n        .select()\n        .from(verificationTokensTable)\n        .where(eq(verificationTokensTable.identifier, data.identifier))\n        .then((res) => res[0])\n    },\n    async useVerificationToken(params: { identifier: string; token: string }) {\n      const deletedToken = await client\n        .select()\n        .from(verificationTokensTable)\n        .where(\n          and(\n            eq(verificationTokensTable.identifier, params.identifier),\n            eq(verificationTokensTable.token, params.token)\n          )\n        )\n        .then((res) => (res.length > 0 ? res[0] : null))\n\n      if (deletedToken) {\n        await client\n          .delete(verificationTokensTable)\n          .where(\n            and(\n              eq(verificationTokensTable.identifier, params.identifier),\n              eq(verificationTokensTable.token, params.token)\n            )\n          )\n      }\n\n      return deletedToken\n    },\n    async deleteUser(id: string) {\n      await client.delete(usersTable).where(eq(usersTable.id, id))\n    },\n    async unlinkAccount(\n      params: Pick<AdapterAccount, \"provider\" | \"providerAccountId\">\n    ) {\n      await client\n        .delete(accountsTable)\n        .where(\n          and(\n            eq(accountsTable.provider, params.provider),\n            eq(accountsTable.providerAccountId, params.providerAccountId)\n          )\n        )\n    },\n    async getAccount(providerAccountId: string, provider: string) {\n      return client\n        .select()\n        .from(accountsTable)\n        .where(\n          and(\n            eq(accountsTable.provider, provider),\n            eq(accountsTable.providerAccountId, providerAccountId)\n          )\n        )\n        .then((res) => res[0] ?? null) as Promise<AdapterAccount | null>\n    },\n    async createAuthenticator(data: AdapterAuthenticator) {\n      await client.insert(authenticatorsTable).values(data)\n\n      return (await client\n        .select()\n        .from(authenticatorsTable)\n        .where(eq(authenticatorsTable.credentialID, data.credentialID))\n        .then((res) => res[0] ?? null)) as Awaitable<AdapterAuthenticator>\n    },\n    async getAuthenticator(credentialID: string) {\n      return (await client\n        .select()\n        .from(authenticatorsTable)\n        .where(eq(authenticatorsTable.credentialID, credentialID))\n        .then(\n          (res) => res[0] ?? null\n        )) as Awaitable<AdapterAuthenticator | null>\n    },\n    async listAuthenticatorsByUserId(userId: string) {\n      return (await client\n        .select()\n        .from(authenticatorsTable)\n        .where(eq(authenticatorsTable.userId, userId))\n        .then((res) => res)) as Awaitable<AdapterAuthenticator[]>\n    },\n    async updateAuthenticatorCounter(credentialID: string, newCounter: number) {\n      await client\n        .update(authenticatorsTable)\n        .set({ counter: newCounter })\n        .where(eq(authenticatorsTable.credentialID, credentialID))\n\n      const authenticator = await client\n        .select()\n        .from(authenticatorsTable)\n        .where(eq(authenticatorsTable.credentialID, credentialID))\n        .then((res) => res[0])\n\n      if (!authenticator) throw new Error(\"Authenticator not found.\")\n\n      return authenticator as Awaitable<AdapterAuthenticator>\n    },\n  }\n}\n\ntype DefaultMyqlColumn<\n  T extends {\n    data: string | number | boolean | Date\n    dataType: \"string\" | \"number\" | \"boolean\" | \"date\"\n    notNull: boolean\n    isPrimaryKey?: boolean\n    columnType:\n      | \"MySqlVarChar\"\n      | \"MySqlText\"\n      | \"MySqlBoolean\"\n      | \"MySqlTimestamp\"\n      | \"MySqlInt\"\n  },\n> = MySqlColumn<{\n  isAutoincrement: boolean\n  isPrimaryKey: T[\"isPrimaryKey\"] extends true ? true : false\n  hasRuntimeDefault: boolean\n  generated: GeneratedColumnConfig<T[\"data\"]> | undefined\n  name: string\n  columnType: T[\"columnType\"]\n  data: T[\"data\"]\n  driverParam: string | number | boolean\n  notNull: T[\"notNull\"]\n  hasDefault: boolean\n  enumValues: any\n  dataType: T[\"dataType\"]\n  tableName: string\n}>\n\nexport type DefaultMySqlUsersTable = MySqlTableWithColumns<{\n  name: string\n  columns: {\n    id: DefaultMyqlColumn<{\n      isPrimaryKey: true\n      data: string\n      dataType: \"string\"\n      notNull: true\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n    }>\n    name: DefaultMyqlColumn<{\n      data: string\n      dataType: \"string\"\n      notNull: boolean\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n    }>\n    email: DefaultMyqlColumn<{\n      data: string\n      dataType: \"string\"\n      notNull: boolean\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n    }>\n    emailVerified: DefaultMyqlColumn<{\n      data: Date\n      dataType: \"date\"\n      notNull: boolean\n      columnType: \"MySqlTimestamp\"\n    }>\n    image: DefaultMyqlColumn<{\n      data: string\n      dataType: \"string\"\n      notNull: boolean\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n    }>\n  }\n  dialect: \"mysql\"\n  schema: string | undefined\n}>\n\nexport type DefaultMySqlAccountsTable = MySqlTableWithColumns<{\n  name: string\n  columns: {\n    userId: DefaultMyqlColumn<{\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    type: DefaultMyqlColumn<{\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    provider: DefaultMyqlColumn<{\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    providerAccountId: DefaultMyqlColumn<{\n      dataType: \"string\"\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: true\n    }>\n    refresh_token: DefaultMyqlColumn<{\n      dataType: \"string\"\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: boolean\n    }>\n    access_token: DefaultMyqlColumn<{\n      dataType: \"string\"\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      driverParam: string | number\n      notNull: boolean\n    }>\n    expires_at: DefaultMyqlColumn<{\n      dataType: \"number\"\n      columnType: \"MySqlInt\"\n      data: number\n      notNull: boolean\n    }>\n    token_type: DefaultMyqlColumn<{\n      dataType: \"string\"\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: boolean\n    }>\n    scope: DefaultMyqlColumn<{\n      dataType: \"string\"\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: boolean\n    }>\n    id_token: DefaultMyqlColumn<{\n      dataType: \"string\"\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: boolean\n    }>\n    session_state: DefaultMyqlColumn<{\n      dataType: \"string\"\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: boolean\n    }>\n  }\n  dialect: \"mysql\"\n  schema: string | undefined\n}>\n\nexport type DefaultMySqlSessionsTable = MySqlTableWithColumns<{\n  name: string\n  columns: {\n    sessionToken: DefaultMyqlColumn<{\n      isPrimaryKey: true\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    userId: DefaultMyqlColumn<{\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    expires: DefaultMyqlColumn<{\n      dataType: \"date\"\n      columnType: \"MySqlTimestamp\"\n      data: Date\n      notNull: true\n    }>\n  }\n  dialect: \"mysql\"\n  schema: string | undefined\n}>\n\nexport type DefaultMySqlVerificationTokenTable = MySqlTableWithColumns<{\n  name: string\n  columns: {\n    identifier: DefaultMyqlColumn<{\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    token: DefaultMyqlColumn<{\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    expires: DefaultMyqlColumn<{\n      dataType: \"date\"\n      columnType: \"MySqlTimestamp\"\n      data: Date\n      notNull: true\n    }>\n  }\n  dialect: \"mysql\"\n  schema: string | undefined\n}>\n\nexport type DefaultMySqlAuthenticatorTable = MySqlTableWithColumns<{\n  name: string\n  columns: {\n    credentialID: DefaultMyqlColumn<{\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    userId: DefaultMyqlColumn<{\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    providerAccountId: DefaultMyqlColumn<{\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    credentialPublicKey: DefaultMyqlColumn<{\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    counter: DefaultMyqlColumn<{\n      columnType: \"MySqlInt\"\n      data: number\n      notNull: true\n      dataType: \"number\"\n    }>\n    credentialDeviceType: DefaultMyqlColumn<{\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    credentialBackedUp: DefaultMyqlColumn<{\n      columnType: \"MySqlBoolean\"\n      data: boolean\n      notNull: true\n      dataType: \"boolean\"\n    }>\n    transports: DefaultMyqlColumn<{\n      columnType: \"MySqlVarChar\" | \"MySqlText\"\n      data: string\n      notNull: false\n      dataType: \"string\"\n    }>\n  }\n  dialect: \"mysql\"\n  schema: string | undefined\n}>\n\nexport type DefaultMySqlSchema = {\n  usersTable: DefaultMySqlUsersTable\n  accountsTable: DefaultMySqlAccountsTable\n  sessionsTable?: DefaultMySqlSessionsTable\n  verificationTokensTable?: DefaultMySqlVerificationTokenTable\n  authenticatorsTable?: DefaultMySqlAuthenticatorTable\n}\n"
  },
  {
    "path": "packages/adapter-drizzle/src/lib/pg.ts",
    "content": "import { GeneratedColumnConfig, and, eq, getTableColumns } from \"drizzle-orm\"\nimport {\n  PgColumn,\n  PgDatabase,\n  PgQueryResultHKT,\n  PgTableWithColumns,\n  boolean,\n  integer,\n  pgTable,\n  primaryKey,\n  text,\n  timestamp,\n} from \"drizzle-orm/pg-core\"\n\nimport type {\n  Adapter,\n  AdapterAccount,\n  AdapterAccountType,\n  AdapterAuthenticator,\n  AdapterSession,\n  AdapterUser,\n  VerificationToken,\n} from \"@auth/core/adapters\"\nimport { Awaitable } from \"@auth/core/types\"\n\nexport function defineTables(\n  schema: Partial<DefaultPostgresSchema> = {}\n): Required<DefaultPostgresSchema> {\n  const usersTable =\n    schema.usersTable ??\n    (pgTable(\"user\", {\n      id: text(\"id\")\n        .primaryKey()\n        .$defaultFn(() => crypto.randomUUID()),\n      name: text(\"name\"),\n      email: text(\"email\").unique(),\n      emailVerified: timestamp(\"emailVerified\", { mode: \"date\" }),\n      image: text(\"image\"),\n    }) satisfies DefaultPostgresUsersTable)\n\n  const accountsTable =\n    schema.accountsTable ??\n    (pgTable(\n      \"account\",\n      {\n        userId: text(\"userId\")\n          .notNull()\n          .references(() => usersTable.id, { onDelete: \"cascade\" }),\n        type: text(\"type\").$type<AdapterAccountType>().notNull(),\n        provider: text(\"provider\").notNull(),\n        providerAccountId: text(\"providerAccountId\").notNull(),\n        refresh_token: text(\"refresh_token\"),\n        access_token: text(\"access_token\"),\n        expires_at: integer(\"expires_at\"),\n        token_type: text(\"token_type\"),\n        scope: text(\"scope\"),\n        id_token: text(\"id_token\"),\n        session_state: text(\"session_state\"),\n      },\n      (account) => ({\n        compositePk: primaryKey({\n          columns: [account.provider, account.providerAccountId],\n        }),\n      })\n    ) satisfies DefaultPostgresAccountsTable)\n\n  const sessionsTable =\n    schema.sessionsTable ??\n    (pgTable(\"session\", {\n      sessionToken: text(\"sessionToken\").primaryKey(),\n      userId: text(\"userId\")\n        .notNull()\n        .references(() => usersTable.id, { onDelete: \"cascade\" }),\n      expires: timestamp(\"expires\", { mode: \"date\" }).notNull(),\n    }) satisfies DefaultPostgresSessionsTable)\n\n  const verificationTokensTable =\n    schema.verificationTokensTable ??\n    (pgTable(\n      \"verificationToken\",\n      {\n        identifier: text(\"identifier\").notNull(),\n        token: text(\"token\").notNull(),\n        expires: timestamp(\"expires\", { mode: \"date\" }).notNull(),\n      },\n      (verificationToken) => ({\n        compositePk: primaryKey({\n          columns: [verificationToken.identifier, verificationToken.token],\n        }),\n      })\n    ) satisfies DefaultPostgresVerificationTokenTable)\n\n  const authenticatorsTable =\n    schema.authenticatorsTable ??\n    (pgTable(\n      \"authenticator\",\n      {\n        credentialID: text(\"credentialID\").notNull().unique(),\n        userId: text(\"userId\")\n          .notNull()\n          .references(() => usersTable.id, { onDelete: \"cascade\" }),\n        providerAccountId: text(\"providerAccountId\").notNull(),\n        credentialPublicKey: text(\"credentialPublicKey\").notNull(),\n        counter: integer(\"counter\").notNull(),\n        credentialDeviceType: text(\"credentialDeviceType\").notNull(),\n        credentialBackedUp: boolean(\"credentialBackedUp\").notNull(),\n        transports: text(\"transports\"),\n      },\n      (authenticator) => ({\n        compositePK: primaryKey({\n          columns: [authenticator.userId, authenticator.credentialID],\n        }),\n      })\n    ) satisfies DefaultPostgresAuthenticatorTable)\n\n  return {\n    usersTable,\n    accountsTable,\n    sessionsTable,\n    verificationTokensTable,\n    authenticatorsTable,\n  }\n}\n\nexport function PostgresDrizzleAdapter(\n  client: PgDatabase<PgQueryResultHKT, any>,\n  schema?: DefaultPostgresSchema\n): Adapter {\n  const {\n    usersTable,\n    accountsTable,\n    sessionsTable,\n    verificationTokensTable,\n    authenticatorsTable,\n  } = defineTables(schema)\n\n  return {\n    async createUser(data: AdapterUser) {\n      const { id, ...insertData } = data\n      const hasDefaultId = getTableColumns(usersTable)[\"id\"][\"hasDefault\"]\n\n      return client\n        .insert(usersTable)\n        .values(hasDefaultId ? insertData : { ...insertData, id })\n        .returning()\n        .then((res) => res[0]) as Awaitable<AdapterUser>\n    },\n    async getUser(userId: string) {\n      return client\n        .select()\n        .from(usersTable)\n        .where(eq(usersTable.id, userId))\n        .then((res) =>\n          res.length > 0 ? res[0] : null\n        ) as Awaitable<AdapterUser | null>\n    },\n    async getUserByEmail(email: string) {\n      return client\n        .select()\n        .from(usersTable)\n        .where(eq(usersTable.email, email))\n        .then((res) =>\n          res.length > 0 ? res[0] : null\n        ) as Awaitable<AdapterUser | null>\n    },\n    async createSession(data: {\n      sessionToken: string\n      userId: string\n      expires: Date\n    }) {\n      return client\n        .insert(sessionsTable)\n        .values(data)\n        .returning()\n        .then((res) => res[0])\n    },\n    async getSessionAndUser(sessionToken: string) {\n      return client\n        .select({\n          session: sessionsTable,\n          user: usersTable,\n        })\n        .from(sessionsTable)\n        .where(eq(sessionsTable.sessionToken, sessionToken))\n        .innerJoin(usersTable, eq(usersTable.id, sessionsTable.userId))\n        .then((res) => (res.length > 0 ? res[0] : null)) as Awaitable<{\n        session: AdapterSession\n        user: AdapterUser\n      } | null>\n    },\n    async updateUser(data: Partial<AdapterUser> & Pick<AdapterUser, \"id\">) {\n      if (!data.id) {\n        throw new Error(\"No user id.\")\n      }\n\n      const [result] = await client\n        .update(usersTable)\n        .set(data)\n        .where(eq(usersTable.id, data.id))\n        .returning()\n\n      if (!result) {\n        throw new Error(\"No user found.\")\n      }\n\n      return result as Awaitable<AdapterUser>\n    },\n    async updateSession(\n      data: Partial<AdapterSession> & Pick<AdapterSession, \"sessionToken\">\n    ) {\n      return client\n        .update(sessionsTable)\n        .set(data)\n        .where(eq(sessionsTable.sessionToken, data.sessionToken))\n        .returning()\n        .then((res) => res[0])\n    },\n    async linkAccount(data: AdapterAccount) {\n      await client.insert(accountsTable).values(data)\n    },\n    async getUserByAccount(\n      account: Pick<AdapterAccount, \"provider\" | \"providerAccountId\">\n    ) {\n      const result = await client\n        .select({\n          account: accountsTable,\n          user: usersTable,\n        })\n        .from(accountsTable)\n        .innerJoin(usersTable, eq(accountsTable.userId, usersTable.id))\n        .where(\n          and(\n            eq(accountsTable.provider, account.provider),\n            eq(accountsTable.providerAccountId, account.providerAccountId)\n          )\n        )\n        .then((res) => res[0])\n\n      const user = result?.user ?? null\n      return user as Awaitable<AdapterUser | null>\n    },\n    async deleteSession(sessionToken: string) {\n      await client\n        .delete(sessionsTable)\n        .where(eq(sessionsTable.sessionToken, sessionToken))\n    },\n    async createVerificationToken(data: VerificationToken) {\n      return client\n        .insert(verificationTokensTable)\n        .values(data)\n        .returning()\n        .then((res) => res[0])\n    },\n    async useVerificationToken(params: { identifier: string; token: string }) {\n      return client\n        .delete(verificationTokensTable)\n        .where(\n          and(\n            eq(verificationTokensTable.identifier, params.identifier),\n            eq(verificationTokensTable.token, params.token)\n          )\n        )\n        .returning()\n        .then((res) => (res.length > 0 ? res[0] : null))\n    },\n    async deleteUser(id: string) {\n      await client.delete(usersTable).where(eq(usersTable.id, id))\n    },\n    async unlinkAccount(\n      params: Pick<AdapterAccount, \"provider\" | \"providerAccountId\">\n    ) {\n      await client\n        .delete(accountsTable)\n        .where(\n          and(\n            eq(accountsTable.provider, params.provider),\n            eq(accountsTable.providerAccountId, params.providerAccountId)\n          )\n        )\n    },\n    async getAccount(providerAccountId: string, provider: string) {\n      return client\n        .select()\n        .from(accountsTable)\n        .where(\n          and(\n            eq(accountsTable.provider, provider),\n            eq(accountsTable.providerAccountId, providerAccountId)\n          )\n        )\n        .then((res) => res[0] ?? null) as Promise<AdapterAccount | null>\n    },\n    async createAuthenticator(data: AdapterAuthenticator) {\n      return client\n        .insert(authenticatorsTable)\n        .values(data)\n        .returning()\n        .then((res) => res[0] ?? null) as Awaitable<AdapterAuthenticator>\n    },\n    async getAuthenticator(credentialID: string) {\n      return client\n        .select()\n        .from(authenticatorsTable)\n        .where(eq(authenticatorsTable.credentialID, credentialID))\n        .then((res) => res[0] ?? null) as Awaitable<AdapterAuthenticator | null>\n    },\n    async listAuthenticatorsByUserId(userId: string) {\n      return client\n        .select()\n        .from(authenticatorsTable)\n        .where(eq(authenticatorsTable.userId, userId))\n        .then((res) => res) as Awaitable<AdapterAuthenticator[]>\n    },\n    async updateAuthenticatorCounter(credentialID: string, newCounter: number) {\n      const authenticator = await client\n        .update(authenticatorsTable)\n        .set({ counter: newCounter })\n        .where(eq(authenticatorsTable.credentialID, credentialID))\n        .returning()\n        .then((res) => res[0])\n\n      if (!authenticator) throw new Error(\"Authenticator not found.\")\n\n      return authenticator as Awaitable<AdapterAuthenticator>\n    },\n  }\n}\n\ntype DefaultPostgresColumn<\n  T extends {\n    data: string | number | boolean | Date\n    dataType: \"string\" | \"number\" | \"boolean\" | \"date\"\n    notNull: boolean\n    isPrimaryKey?: boolean\n    columnType:\n      | \"PgVarchar\"\n      | \"PgText\"\n      | \"PgBoolean\"\n      | \"PgTimestamp\"\n      | \"PgInteger\"\n      | \"PgUUID\"\n  },\n> = PgColumn<{\n  name: string\n  isAutoincrement: boolean\n  isPrimaryKey: T[\"isPrimaryKey\"] extends true ? true : false\n  hasRuntimeDefault: boolean\n  generated: GeneratedColumnConfig<T[\"data\"]> | undefined\n  columnType: T[\"columnType\"]\n  data: T[\"data\"]\n  driverParam: string | number | boolean\n  notNull: T[\"notNull\"]\n  hasDefault: boolean\n  enumValues: any\n  dataType: T[\"dataType\"]\n  tableName: string\n}>\n\nexport type DefaultPostgresUsersTable = PgTableWithColumns<{\n  name: string\n  columns: {\n    id: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\" | \"PgUUID\"\n      isPrimaryKey: true\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    name: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: boolean\n      dataType: \"string\"\n    }>\n    email: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: boolean\n      dataType: \"string\"\n    }>\n    emailVerified: DefaultPostgresColumn<{\n      dataType: \"date\"\n      columnType: \"PgTimestamp\"\n      data: Date\n      notNull: boolean\n    }>\n    image: DefaultPostgresColumn<{\n      dataType: \"string\"\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: boolean\n    }>\n  }\n  dialect: \"pg\"\n  schema: string | undefined\n}>\n\nexport type DefaultPostgresAccountsTable = PgTableWithColumns<{\n  name: string\n  columns: {\n    userId: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\" | \"PgUUID\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    type: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    provider: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    providerAccountId: DefaultPostgresColumn<{\n      dataType: \"string\"\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: true\n    }>\n    refresh_token: DefaultPostgresColumn<{\n      dataType: \"string\"\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: boolean\n    }>\n    access_token: DefaultPostgresColumn<{\n      dataType: \"string\"\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: boolean\n    }>\n    expires_at: DefaultPostgresColumn<{\n      dataType: \"number\"\n      columnType: \"PgInteger\"\n      data: number\n      notNull: boolean\n    }>\n    token_type: DefaultPostgresColumn<{\n      dataType: \"string\"\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: boolean\n    }>\n    scope: DefaultPostgresColumn<{\n      dataType: \"string\"\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: boolean\n    }>\n    id_token: DefaultPostgresColumn<{\n      dataType: \"string\"\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: boolean\n    }>\n    session_state: DefaultPostgresColumn<{\n      dataType: \"string\"\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: boolean\n    }>\n  }\n  dialect: \"pg\"\n  schema: string | undefined\n}>\n\nexport type DefaultPostgresSessionsTable = PgTableWithColumns<{\n  name: string\n  columns: {\n    sessionToken: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      isPrimaryKey: true\n      notNull: true\n      dataType: \"string\"\n    }>\n    userId: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\" | \"PgUUID\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    expires: DefaultPostgresColumn<{\n      dataType: \"date\"\n      columnType: \"PgTimestamp\"\n      data: Date\n      notNull: true\n    }>\n  }\n  dialect: \"pg\"\n  schema: string | undefined\n}>\n\nexport type DefaultPostgresVerificationTokenTable = PgTableWithColumns<{\n  name: string\n  columns: {\n    identifier: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    token: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    expires: DefaultPostgresColumn<{\n      dataType: \"date\"\n      columnType: \"PgTimestamp\"\n      data: Date\n      notNull: true\n    }>\n  }\n  dialect: \"pg\"\n  schema: string | undefined\n}>\n\nexport type DefaultPostgresAuthenticatorTable = PgTableWithColumns<{\n  name: string\n  columns: {\n    credentialID: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    userId: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\" | \"PgUUID\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    providerAccountId: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    credentialPublicKey: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    counter: DefaultPostgresColumn<{\n      columnType: \"PgInteger\"\n      data: number\n      notNull: true\n      dataType: \"number\"\n    }>\n    credentialDeviceType: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    credentialBackedUp: DefaultPostgresColumn<{\n      columnType: \"PgBoolean\"\n      data: boolean\n      notNull: true\n      dataType: \"boolean\"\n    }>\n    transports: DefaultPostgresColumn<{\n      columnType: \"PgVarchar\" | \"PgText\"\n      data: string\n      notNull: false\n      dataType: \"string\"\n    }>\n  }\n  dialect: \"pg\"\n  schema: string | undefined\n}>\n\nexport type DefaultPostgresSchema = {\n  usersTable: DefaultPostgresUsersTable\n  accountsTable: DefaultPostgresAccountsTable\n  sessionsTable?: DefaultPostgresSessionsTable\n  verificationTokensTable?: DefaultPostgresVerificationTokenTable\n  authenticatorsTable?: DefaultPostgresAuthenticatorTable\n}\n"
  },
  {
    "path": "packages/adapter-drizzle/src/lib/sqlite.ts",
    "content": "import { GeneratedColumnConfig, and, eq, getTableColumns } from \"drizzle-orm\"\nimport {\n  BaseSQLiteDatabase,\n  SQLiteColumn,\n  SQLiteTableWithColumns,\n  integer,\n  primaryKey,\n  sqliteTable,\n  text,\n} from \"drizzle-orm/sqlite-core\"\n\nimport type {\n  Adapter,\n  AdapterAccount,\n  AdapterAccountType,\n  AdapterAuthenticator,\n  AdapterSession,\n  AdapterUser,\n  VerificationToken,\n} from \"@auth/core/adapters\"\nimport { Awaitable } from \"@auth/core/types\"\n\nexport function defineTables(\n  schema: Partial<DefaultSQLiteSchema> = {}\n): Required<DefaultSQLiteSchema> {\n  const usersTable =\n    schema.usersTable ??\n    (sqliteTable(\"user\", {\n      id: text(\"id\")\n        .primaryKey()\n        .$defaultFn(() => crypto.randomUUID()),\n      name: text(\"name\"),\n      email: text(\"email\").unique(),\n      emailVerified: integer(\"emailVerified\", { mode: \"timestamp_ms\" }),\n      image: text(\"image\"),\n    }) satisfies DefaultSQLiteUsersTable)\n\n  const accountsTable =\n    schema.accountsTable ??\n    (sqliteTable(\n      \"account\",\n      {\n        userId: text(\"userId\")\n          .notNull()\n          .references(() => usersTable.id, { onDelete: \"cascade\" }),\n        type: text(\"type\").$type<AdapterAccountType>().notNull(),\n        provider: text(\"provider\").notNull(),\n        providerAccountId: text(\"providerAccountId\").notNull(),\n        refresh_token: text(\"refresh_token\"),\n        access_token: text(\"access_token\"),\n        expires_at: integer(\"expires_at\"),\n        token_type: text(\"token_type\"),\n        scope: text(\"scope\"),\n        id_token: text(\"id_token\"),\n        session_state: text(\"session_state\"),\n      },\n      (account) => ({\n        compositePk: primaryKey({\n          columns: [account.provider, account.providerAccountId],\n        }),\n      })\n    ) satisfies DefaultSQLiteAccountsTable)\n\n  const sessionsTable =\n    schema.sessionsTable ??\n    (sqliteTable(\"session\", {\n      sessionToken: text(\"sessionToken\").primaryKey(),\n      userId: text(\"userId\")\n        .notNull()\n        .references(() => usersTable.id, { onDelete: \"cascade\" }),\n      expires: integer(\"expires\", { mode: \"timestamp_ms\" }).notNull(),\n    }) satisfies DefaultSQLiteSessionsTable)\n\n  const verificationTokensTable =\n    schema.verificationTokensTable ??\n    (sqliteTable(\n      \"verificationToken\",\n      {\n        identifier: text(\"identifier\").notNull(),\n        token: text(\"token\").notNull(),\n        expires: integer(\"expires\", { mode: \"timestamp_ms\" }).notNull(),\n      },\n      (verficationToken) => ({\n        compositePk: primaryKey({\n          columns: [verficationToken.identifier, verficationToken.token],\n        }),\n      })\n    ) satisfies DefaultSQLiteVerificationTokenTable)\n\n  const authenticatorsTable =\n    schema.authenticatorsTable ??\n    (sqliteTable(\n      \"authenticator\",\n      {\n        credentialID: text(\"credentialID\").notNull().unique(),\n        userId: text(\"userId\")\n          .notNull()\n          .references(() => usersTable.id, { onDelete: \"cascade\" }),\n        providerAccountId: text(\"providerAccountId\").notNull(),\n        credentialPublicKey: text(\"credentialPublicKey\").notNull(),\n        counter: integer(\"counter\").notNull(),\n        credentialDeviceType: text(\"credentialDeviceType\").notNull(),\n        credentialBackedUp: integer(\"credentialBackedUp\", {\n          mode: \"boolean\",\n        }).notNull(),\n        transports: text(\"transports\"),\n      },\n      (authenticator) => ({\n        compositePK: primaryKey({\n          columns: [authenticator.userId, authenticator.credentialID],\n        }),\n      })\n    ) satisfies DefaultSQLiteAuthenticatorTable)\n\n  return {\n    usersTable,\n    accountsTable,\n    sessionsTable,\n    verificationTokensTable,\n    authenticatorsTable,\n  }\n}\n\nexport function SQLiteDrizzleAdapter(\n  client: BaseSQLiteDatabase<\"sync\" | \"async\", any, any>,\n  schema?: DefaultSQLiteSchema\n): Adapter {\n  const {\n    usersTable,\n    accountsTable,\n    sessionsTable,\n    verificationTokensTable,\n    authenticatorsTable,\n  } = defineTables(schema)\n\n  return {\n    async createUser(data: AdapterUser) {\n      const { id, ...insertData } = data\n      const hasDefaultId = getTableColumns(usersTable)[\"id\"][\"hasDefault\"]\n\n      return client\n        .insert(usersTable)\n        .values(hasDefaultId ? insertData : { ...insertData, id })\n        .returning()\n        .get() as Awaitable<AdapterUser>\n    },\n    async getUser(userId: string) {\n      const result =\n        (await client\n          .select()\n          .from(usersTable)\n          .where(eq(usersTable.id, userId))\n          .get()) ?? null\n\n      return result as Awaitable<AdapterUser | null>\n    },\n    async getUserByEmail(email: string) {\n      const result =\n        (await client\n          .select()\n          .from(usersTable)\n          .where(eq(usersTable.email, email))\n          .get()) ?? null\n\n      return result as Awaitable<AdapterUser | null>\n    },\n    async createSession(data: {\n      sessionToken: string\n      userId: string\n      expires: Date\n    }) {\n      return client.insert(sessionsTable).values(data).returning().get()\n    },\n    async getSessionAndUser(sessionToken: string) {\n      const result =\n        (await client\n          .select({\n            session: sessionsTable,\n            user: usersTable,\n          })\n          .from(sessionsTable)\n          .where(eq(sessionsTable.sessionToken, sessionToken))\n          .innerJoin(usersTable, eq(usersTable.id, sessionsTable.userId))\n          .get()) ?? null\n\n      return result as Awaitable<{\n        session: AdapterSession\n        user: AdapterUser\n      } | null>\n    },\n    async updateUser(data: Partial<AdapterUser> & Pick<AdapterUser, \"id\">) {\n      if (!data.id) {\n        throw new Error(\"No user id.\")\n      }\n\n      const result = await client\n        .update(usersTable)\n        .set(data)\n        .where(eq(usersTable.id, data.id))\n        .returning()\n        .get()\n\n      if (!result) {\n        throw new Error(\"User not found.\")\n      }\n\n      return result as Awaitable<AdapterUser>\n    },\n    async updateSession(\n      data: Partial<AdapterSession> & Pick<AdapterSession, \"sessionToken\">\n    ) {\n      const result = await client\n        .update(sessionsTable)\n        .set(data)\n        .where(eq(sessionsTable.sessionToken, data.sessionToken))\n        .returning()\n        .get()\n\n      return result ?? null\n    },\n    async linkAccount(data: AdapterAccount) {\n      await client.insert(accountsTable).values(data).run()\n    },\n    async getUserByAccount(\n      account: Pick<AdapterAccount, \"provider\" | \"providerAccountId\">\n    ) {\n      const result = await client\n        .select({\n          account: accountsTable,\n          user: usersTable,\n        })\n        .from(accountsTable)\n        .innerJoin(usersTable, eq(accountsTable.userId, usersTable.id))\n        .where(\n          and(\n            eq(accountsTable.provider, account.provider),\n            eq(accountsTable.providerAccountId, account.providerAccountId)\n          )\n        )\n        .get()\n\n      const user = result?.user ?? null\n\n      return user as Awaitable<AdapterUser | null>\n    },\n    async deleteSession(sessionToken: string) {\n      await client\n        .delete(sessionsTable)\n        .where(eq(sessionsTable.sessionToken, sessionToken))\n        .run()\n    },\n    async createVerificationToken(data: VerificationToken) {\n      return client\n        .insert(verificationTokensTable)\n        .values(data)\n        .returning()\n        .get()\n    },\n    async useVerificationToken(params: { identifier: string; token: string }) {\n      const result = await client\n        .delete(verificationTokensTable)\n        .where(\n          and(\n            eq(verificationTokensTable.identifier, params.identifier),\n            eq(verificationTokensTable.token, params.token)\n          )\n        )\n        .returning()\n        .get()\n\n      return result ?? null\n    },\n    async deleteUser(id: string) {\n      await client.delete(usersTable).where(eq(usersTable.id, id)).run()\n    },\n    async unlinkAccount(\n      params: Pick<AdapterAccount, \"provider\" | \"providerAccountId\">\n    ) {\n      await client\n        .delete(accountsTable)\n        .where(\n          and(\n            eq(accountsTable.provider, params.provider),\n            eq(accountsTable.providerAccountId, params.providerAccountId)\n          )\n        )\n        .run()\n    },\n    async getAccount(providerAccountId: string, provider: string) {\n      return client\n        .select()\n        .from(accountsTable)\n        .where(\n          and(\n            eq(accountsTable.provider, provider),\n            eq(accountsTable.providerAccountId, providerAccountId)\n          )\n        )\n        .then((res) => res[0] ?? null) as Promise<AdapterAccount | null>\n    },\n    async createAuthenticator(data: AdapterAuthenticator) {\n      return client\n        .insert(authenticatorsTable)\n        .values(data)\n        .returning()\n        .then((res) => res[0] ?? null) as Awaitable<AdapterAuthenticator>\n    },\n    async getAuthenticator(credentialID: string) {\n      return client\n        .select()\n        .from(authenticatorsTable)\n        .where(eq(authenticatorsTable.credentialID, credentialID))\n        .then((res) => res[0] ?? null) as Awaitable<AdapterAuthenticator | null>\n    },\n    async listAuthenticatorsByUserId(userId: string) {\n      return client\n        .select()\n        .from(authenticatorsTable)\n        .where(eq(authenticatorsTable.userId, userId))\n        .then((res) => res) as Awaitable<AdapterAuthenticator[]>\n    },\n    async updateAuthenticatorCounter(credentialID: string, newCounter: number) {\n      const authenticator = await client\n        .update(authenticatorsTable)\n        .set({ counter: newCounter })\n        .where(eq(authenticatorsTable.credentialID, credentialID))\n        .returning()\n        .then((res) => res[0])\n\n      if (!authenticator) throw new Error(\"Authenticator not found.\")\n\n      return authenticator as Awaitable<AdapterAuthenticator>\n    },\n  }\n}\n\ntype DefaultSQLiteColumn<\n  T extends {\n    data: string | boolean | number | Date\n    dataType: \"string\" | \"boolean\" | \"number\" | \"date\"\n    notNull: boolean\n    isPrimaryKey?: boolean\n    columnType:\n      | \"SQLiteText\"\n      | \"SQLiteBoolean\"\n      | \"SQLiteTimestamp\"\n      | \"SQLiteInteger\"\n  },\n> = SQLiteColumn<{\n  name: string\n  isAutoincrement: boolean\n  isPrimaryKey: T[\"isPrimaryKey\"] extends true ? true : false\n  hasRuntimeDefault: boolean\n  generated: GeneratedColumnConfig<T[\"data\"]> | undefined\n  columnType: T[\"columnType\"]\n  data: T[\"data\"]\n  driverParam: string | number | boolean\n  notNull: T[\"notNull\"]\n  hasDefault: boolean\n  enumValues: any\n  dataType: T[\"dataType\"]\n  tableName: string\n}>\n\nexport type DefaultSQLiteUsersTable = SQLiteTableWithColumns<{\n  name: string\n  columns: {\n    id: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      isPrimaryKey: true\n      notNull: true\n      dataType: \"string\"\n    }>\n    name: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: boolean\n      dataType: \"string\"\n    }>\n    email: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: boolean\n      dataType: \"string\"\n    }>\n    emailVerified: DefaultSQLiteColumn<{\n      dataType: \"date\"\n      columnType: \"SQLiteTimestamp\"\n      data: Date\n      notNull: boolean\n    }>\n    image: DefaultSQLiteColumn<{\n      dataType: \"string\"\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: boolean\n    }>\n  }\n  dialect: \"sqlite\"\n  schema: string | undefined\n}>\n\nexport type DefaultSQLiteAccountsTable = SQLiteTableWithColumns<{\n  name: string\n  columns: {\n    userId: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    type: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    provider: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    providerAccountId: DefaultSQLiteColumn<{\n      dataType: \"string\"\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: true\n    }>\n    refresh_token: DefaultSQLiteColumn<{\n      dataType: \"string\"\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: boolean\n    }>\n    access_token: DefaultSQLiteColumn<{\n      dataType: \"string\"\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: boolean\n    }>\n    expires_at: DefaultSQLiteColumn<{\n      dataType: \"number\"\n      columnType: \"SQLiteInteger\"\n      data: number\n      notNull: boolean\n    }>\n    token_type: DefaultSQLiteColumn<{\n      dataType: \"string\"\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: boolean\n    }>\n    scope: DefaultSQLiteColumn<{\n      dataType: \"string\"\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: boolean\n    }>\n    id_token: DefaultSQLiteColumn<{\n      dataType: \"string\"\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: boolean\n    }>\n    session_state: DefaultSQLiteColumn<{\n      dataType: \"string\"\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: boolean\n    }>\n  }\n  dialect: \"sqlite\"\n  schema: string | undefined\n}>\n\nexport type DefaultSQLiteSessionsTable = SQLiteTableWithColumns<{\n  name: string\n  columns: {\n    sessionToken: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      isPrimaryKey: true\n      notNull: true\n      dataType: \"string\"\n    }>\n    userId: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    expires: DefaultSQLiteColumn<{\n      dataType: \"date\"\n      columnType: \"SQLiteTimestamp\"\n      data: Date\n      notNull: true\n    }>\n  }\n  dialect: \"sqlite\"\n  schema: string | undefined\n}>\n\nexport type DefaultSQLiteVerificationTokenTable = SQLiteTableWithColumns<{\n  name: string\n  columns: {\n    identifier: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    token: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    expires: DefaultSQLiteColumn<{\n      dataType: \"date\"\n      columnType: \"SQLiteTimestamp\"\n      data: Date\n      notNull: true\n    }>\n  }\n  dialect: \"sqlite\"\n  schema: string | undefined\n}>\n\nexport type DefaultSQLiteAuthenticatorTable = SQLiteTableWithColumns<{\n  name: string\n  columns: {\n    credentialID: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    userId: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    providerAccountId: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    credentialPublicKey: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    counter: DefaultSQLiteColumn<{\n      columnType: \"SQLiteInteger\"\n      data: number\n      notNull: true\n      dataType: \"number\"\n    }>\n    credentialDeviceType: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: true\n      dataType: \"string\"\n    }>\n    credentialBackedUp: DefaultSQLiteColumn<{\n      columnType: \"SQLiteBoolean\"\n      data: boolean\n      notNull: true\n      dataType: \"boolean\"\n    }>\n    transports: DefaultSQLiteColumn<{\n      columnType: \"SQLiteText\"\n      data: string\n      notNull: false\n      dataType: \"string\"\n    }>\n  }\n  dialect: \"sqlite\"\n  schema: string | undefined\n}>\n\nexport type DefaultSQLiteSchema = {\n  usersTable: DefaultSQLiteUsersTable\n  accountsTable: DefaultSQLiteAccountsTable\n  sessionsTable?: DefaultSQLiteSessionsTable\n  verificationTokensTable?: DefaultSQLiteVerificationTokenTable\n  authenticatorsTable?: DefaultSQLiteAuthenticatorTable\n}\n"
  },
  {
    "path": "packages/adapter-drizzle/src/lib/utils.ts",
    "content": "import type {\n  MySqlQueryResultHKT,\n  PreparedQueryHKTBase,\n} from \"drizzle-orm/mysql-core\"\nimport { MySqlDatabase } from \"drizzle-orm/mysql-core\"\nimport { PgDatabase, PgQueryResultHKT } from \"drizzle-orm/pg-core\"\nimport { BaseSQLiteDatabase } from \"drizzle-orm/sqlite-core\"\nimport { DefaultMySqlSchema } from \"./mysql.js\"\nimport { DefaultPostgresSchema } from \"./pg.js\"\nimport { DefaultSQLiteSchema } from \"./sqlite.js\"\n\ntype AnyPostgresDatabase = PgDatabase<PgQueryResultHKT, any>\ntype AnyMySqlDatabase = MySqlDatabase<\n  MySqlQueryResultHKT,\n  PreparedQueryHKTBase,\n  any\n>\ntype AnySQLiteDatabase = BaseSQLiteDatabase<\"sync\" | \"async\", any, any>\n\nexport type SqlFlavorOptions =\n  | AnyPostgresDatabase\n  | AnyMySqlDatabase\n  | AnySQLiteDatabase\n\nexport type DefaultSchema<Flavor extends SqlFlavorOptions> =\n  Flavor extends AnyMySqlDatabase\n    ? DefaultMySqlSchema\n    : Flavor extends AnyPostgresDatabase\n      ? DefaultPostgresSchema\n      : Flavor extends AnySQLiteDatabase\n        ? DefaultSQLiteSchema\n        : never\n"
  },
  {
    "path": "packages/adapter-drizzle/test/fixtures.ts",
    "content": "// This work is needed as workaround to Drizzle truncating millisecond precision.\n// https://github.com/drizzle-team/drizzle-orm/pull/668\n\nimport { randomUUID } from \"utils/adapter\"\n\nconst emailVerified = new Date()\nemailVerified.setMilliseconds(0)\n\nconst ONE_WEEK_FROM_NOW = new Date(Date.now() + 1000 * 60 * 60 * 24 * 7)\nONE_WEEK_FROM_NOW.setMilliseconds(0)\nconst FIFTEEN_MINUTES_FROM_NOW = new Date(Date.now() + 15 * 60 * 1000)\nFIFTEEN_MINUTES_FROM_NOW.setMilliseconds(0)\n\nconst ONE_MONTH = 1000 * 60 * 60 * 24 * 30\nconst ONE_MONTH_FROM_NOW = new Date(Date.now() + ONE_MONTH)\nONE_MONTH_FROM_NOW.setMilliseconds(0)\n\nexport const fixtures = {\n  user: {\n    email: \"fill@murray.com\",\n    image: \"https://www.fillmurray.com/460/300\",\n    name: \"Fill Murray\",\n    emailVerified,\n    id: randomUUID(),\n  },\n  session: {\n    sessionToken: randomUUID(),\n    expires: ONE_WEEK_FROM_NOW,\n  },\n  sessionUpdateExpires: ONE_MONTH_FROM_NOW,\n  verificationTokenExpires: FIFTEEN_MINUTES_FROM_NOW,\n  account: {\n    provider: \"github\",\n    providerAccountId: randomUUID(),\n    type: \"oauth\",\n    access_token: randomUUID(),\n    expires_at: ONE_MONTH / 1000,\n    id_token: randomUUID(),\n    refresh_token: randomUUID(),\n    token_type: \"bearer\",\n    scope: \"user\",\n    session_state: randomUUID(),\n  },\n}\n"
  },
  {
    "path": "packages/adapter-drizzle/test/mysql/drizzle.config.ts",
    "content": "import type { Config } from \"drizzle-kit\"\n\nexport default {\n  schema: \"./test/mysql/schema.ts\",\n  out: \"./test/mysql/.drizzle\",\n  dialect: \"mysql\",\n  dbCredentials: {\n    host: \"localhost\",\n    user: \"root\",\n    password: \"password\",\n    database: \"next-auth\",\n  },\n} satisfies Config\n"
  },
  {
    "path": "packages/adapter-drizzle/test/mysql/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { DrizzleAdapter } from \"../../src\"\nimport {\n  db,\n  sessionsTable as sessions,\n  verificationTokensTable as verificationTokens,\n  accountsTable as accounts,\n  usersTable as users,\n  authenticatorsTable as authenticators,\n} from \"./schema\"\nimport { eq, and } from \"drizzle-orm\"\nimport { fixtures } from \"../fixtures\"\n\nrunBasicTests({\n  adapter: DrizzleAdapter(db),\n  fixtures,\n  testWebAuthnMethods: true,\n  db: {\n    connect: async () => {\n      await Promise.all([\n        db.delete(sessions),\n        db.delete(accounts),\n        db.delete(verificationTokens),\n        db.delete(users),\n        db.delete(authenticators),\n      ])\n    },\n    disconnect: async () => {\n      await db.delete(accounts)\n      await Promise.all([\n        db.delete(sessions),\n        db.delete(verificationTokens),\n        db.delete(users),\n        db.delete(authenticators),\n      ])\n    },\n    user: (id) =>\n      db\n        .select()\n        .from(users)\n        .where(eq(users.id, id))\n        .then((res) => res[0] ?? null),\n    session: (sessionToken) =>\n      db\n        .select()\n        .from(sessions)\n        .where(eq(sessions.sessionToken, sessionToken))\n        .then((res) => res[0] ?? null),\n    account: (provider_providerAccountId) =>\n      db\n        .select()\n        .from(accounts)\n        .where(\n          eq(\n            accounts.providerAccountId,\n            provider_providerAccountId.providerAccountId\n          )\n        )\n        .then((res) => res[0] ?? null),\n    verificationToken: (identifier_token) =>\n      db\n        .select()\n        .from(verificationTokens)\n        .where(\n          and(\n            eq(verificationTokens.token, identifier_token.token),\n            eq(verificationTokens.identifier, identifier_token.identifier)\n          )\n        )\n        .then((res) => res[0]) ?? null,\n    authenticator: (credentialID) =>\n      db\n        .select()\n        .from(authenticators)\n        .where(eq(authenticators.credentialID, credentialID))\n        .then((res) => res[0]),\n  },\n})\n"
  },
  {
    "path": "packages/adapter-drizzle/test/mysql/migrator.ts",
    "content": "import { migrate } from \"drizzle-orm/mysql2/migrator\"\nimport { db } from \"./schema.ts\"\n\nconst migrator = async () => {\n  await migrate(db, { migrationsFolder: \"./test/mysql/.drizzle\" })\n}\n\nmigrator()\n  .then(() => process.exit(0))\n  .catch(() => process.exit(1))\n"
  },
  {
    "path": "packages/adapter-drizzle/test/mysql/schema.ts",
    "content": "import { drizzle } from \"drizzle-orm/mysql2\"\nimport { createPool } from \"mysql2\"\nimport { defineTables } from \"../../src/lib/mysql\"\n\nexport const {\n  usersTable,\n  accountsTable,\n  sessionsTable,\n  verificationTokensTable,\n  authenticatorsTable,\n} = defineTables({})\n\nconst poolConnection = createPool({\n  host: \"127.0.0.1\",\n  user: \"root\",\n  password: \"password\",\n  database: \"next-auth\",\n})\n\nexport const db = drizzle(poolConnection)\n"
  },
  {
    "path": "packages/adapter-drizzle/test/mysql/test.sh",
    "content": "#!/usr/bin/env bash\n\necho \"Initializing container for MySQL tests.\"\n\nMYSQL_DATABASE=next-auth\nMYSQL_ROOT_PASSWORD=password\nMYSQL_CONTAINER_NAME=next-auth-mysql-test\n\ndocker run -d --rm \\\n  -e MYSQL_DATABASE=${MYSQL_DATABASE} \\\n  -e MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} \\\n  --name \"${MYSQL_CONTAINER_NAME}\" \\\n  -p 3306:3306 \\\n  mysql:8\n\necho \"Waiting 15s for db to start...\" && sleep 15\n\ndrizzle-kit generate --config=./test/mysql/drizzle.config.ts\ndrizzle-kit migrate --config=./test/mysql/drizzle.config.ts\n\n# Run Tests\nif vitest run -c ../utils/vitest.config.ts ./test/mysql/index.test.ts; then\n  docker stop ${MYSQL_CONTAINER_NAME}\nelse\n  docker stop ${MYSQL_CONTAINER_NAME} && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-drizzle/test/mysql-multi-project-schema/drizzle.config.ts",
    "content": "import type { Config } from \"drizzle-kit\"\n\nexport default {\n  schema: \"./test/mysql-multi-project-schema/schema.ts\",\n  out: \"./test/mysql-multi-project-schema/.drizzle\",\n  dialect: \"mysql\",\n  dbCredentials: {\n    host: \"localhost\",\n    user: \"root\",\n    password: \"password\",\n    database: \"next-auth\",\n  },\n} satisfies Config\n"
  },
  {
    "path": "packages/adapter-drizzle/test/mysql-multi-project-schema/index.test.ts",
    "content": "import { and, eq } from \"drizzle-orm\"\nimport { runBasicTests } from \"utils/adapter\"\nimport { DrizzleAdapter } from \"../../src\"\nimport { fixtures } from \"../fixtures\"\nimport {\n  accounts,\n  authenticators,\n  db,\n  sessions,\n  users,\n  verificationTokens,\n} from \"./schema\"\n\nrunBasicTests({\n  adapter: DrizzleAdapter(db, {\n    usersTable: users,\n    accountsTable: accounts,\n    sessionsTable: sessions,\n    verificationTokensTable: verificationTokens,\n    authenticatorsTable: authenticators,\n  }),\n  fixtures,\n  testWebAuthnMethods: true,\n  db: {\n    connect: async () => {\n      await Promise.all([\n        db.delete(sessions),\n        db.delete(accounts),\n        db.delete(verificationTokens),\n        db.delete(users),\n        db.delete(authenticators),\n      ])\n    },\n    disconnect: async () => {\n      await Promise.all([\n        db.delete(sessions),\n        db.delete(accounts),\n        db.delete(verificationTokens),\n        db.delete(users),\n        db.delete(authenticators),\n      ])\n    },\n    user: (id) =>\n      db\n        .select()\n        .from(users)\n        .where(eq(users.id, id))\n        .then((res) => res[0] ?? null),\n    session: (sessionToken) =>\n      db\n        .select()\n        .from(sessions)\n        .where(eq(sessions.sessionToken, sessionToken))\n        .then((res) => res[0] ?? null),\n    account: (provider_providerAccountId) =>\n      db\n        .select()\n        .from(accounts)\n        .where(\n          eq(\n            accounts.providerAccountId,\n            provider_providerAccountId.providerAccountId\n          )\n        )\n        .then((res) => res[0] ?? null),\n    verificationToken: (identifier_token) =>\n      db\n        .select()\n        .from(verificationTokens)\n        .where(\n          and(\n            eq(verificationTokens.token, identifier_token.token),\n            eq(verificationTokens.identifier, identifier_token.identifier)\n          )\n        )\n        .then((res) => res[0]) ?? null,\n    authenticator: (credentialID) =>\n      db\n        .select()\n        .from(authenticators)\n        .where(eq(authenticators.credentialID, credentialID))\n        .then((res) => res[0]),\n  },\n})\n"
  },
  {
    "path": "packages/adapter-drizzle/test/mysql-multi-project-schema/migrator.ts",
    "content": "import { migrate } from \"drizzle-orm/mysql2/migrator\"\nimport { db } from \"./schema\"\n\nconst migrator = async () => {\n  await migrate(db, {\n    migrationsFolder: \"./test/mysql-multi-project-schema/.drizzle\",\n  })\n}\n\nmigrator()\n  .then(() => process.exit(0))\n  .catch(() => process.exit(1))\n"
  },
  {
    "path": "packages/adapter-drizzle/test/mysql-multi-project-schema/schema.ts",
    "content": "import type { AdapterAccountType } from \"@auth/core/adapters\"\nimport { sql } from \"drizzle-orm\"\nimport {\n  boolean,\n  int,\n  mysqlTableCreator,\n  primaryKey,\n  timestamp,\n  varchar,\n} from \"drizzle-orm/mysql-core\"\nimport { drizzle } from \"drizzle-orm/mysql2\"\nimport { createPool } from \"mysql2\"\n\nconst poolConnection = createPool({\n  host: \"127.0.0.1\",\n  user: \"root\",\n  password: \"password\",\n  database: \"next-auth\",\n})\n\nexport const db = drizzle(poolConnection)\n\nconst mysqlTable = mysqlTableCreator((name) => `project1_${name}`)\n\nexport const users = mysqlTable(\"user\", {\n  id: varchar(\"id\", { length: 255 })\n    .primaryKey()\n    .$defaultFn(() => crypto.randomUUID()),\n  name: varchar(\"name\", { length: 255 }),\n  email: varchar(\"email\", { length: 255 }).unique(),\n  emailVerified: timestamp(\"emailVerified\", { mode: \"date\", fsp: 3 }),\n  image: varchar(\"image\", { length: 255 }),\n})\n\nexport const accounts = mysqlTable(\n  \"account\",\n  {\n    userId: varchar(\"userId\", { length: 255 })\n      .notNull()\n      .references(() => users.id, { onDelete: \"cascade\" }),\n    type: varchar(\"type\", { length: 255 })\n      .$type<AdapterAccountType>()\n      .notNull(),\n    provider: varchar(\"provider\", { length: 255 }).notNull(),\n    providerAccountId: varchar(\"providerAccountId\", { length: 255 }).notNull(),\n    refresh_token: varchar(\"refresh_token\", { length: 255 }),\n    access_token: varchar(\"access_token\", { length: 255 }),\n    expires_at: int(\"expires_at\"),\n    token_type: varchar(\"token_type\", { length: 255 }),\n    scope: varchar(\"scope\", { length: 255 }),\n    id_token: varchar(\"id_token\", { length: 2048 }),\n    session_state: varchar(\"session_state\", { length: 255 }),\n  },\n  (account) => ({\n    compositePk: primaryKey({\n      columns: [account.provider, account.providerAccountId],\n    }),\n  })\n)\n\nexport const sessions = mysqlTable(\"session\", {\n  sessionToken: varchar(\"sessionToken\", { length: 255 }).primaryKey(),\n  userId: varchar(\"userId\", { length: 255 })\n    .notNull()\n    .references(() => users.id, { onDelete: \"cascade\" }),\n  expires: timestamp(\"expires\", { mode: \"date\" }).notNull(),\n})\n\nexport const verificationTokens = mysqlTable(\n  \"verificationToken\",\n  {\n    identifier: varchar(\"identifier\", { length: 255 }).notNull(),\n    token: varchar(\"token\", { length: 255 }).notNull(),\n    expires: timestamp(\"expires\", { mode: \"date\" }).notNull(),\n  },\n  (vt) => ({\n    compositePk: primaryKey({ columns: [vt.identifier, vt.token] }),\n  })\n)\n\nexport const authenticators = mysqlTable(\n  \"authenticator\",\n  {\n    credentialID: varchar(\"credentialID\", { length: 255 }).notNull().unique(),\n    userId: varchar(\"userId\", { length: 255 })\n      .notNull()\n      .references(() => users.id, { onDelete: \"cascade\" }),\n    providerAccountId: varchar(\"providerAccountId\", { length: 255 }).notNull(),\n    credentialPublicKey: varchar(\"credentialPublicKey\", {\n      length: 255,\n    }).notNull(),\n    counter: int(\"counter\").notNull(),\n    credentialDeviceType: varchar(\"credentialDeviceType\", {\n      length: 255,\n    }).notNull(),\n    credentialBackedUp: boolean(\"credentialBackedUp\").notNull(),\n    transports: varchar(\"transports\", { length: 255 }),\n  },\n  (authenticator) => ({\n    compositePk: primaryKey({\n      columns: [authenticator.userId, authenticator.credentialID],\n    }),\n  })\n)\n"
  },
  {
    "path": "packages/adapter-drizzle/test/mysql-multi-project-schema/test.sh",
    "content": "#!/usr/bin/env bash\n\necho \"Initializing container for MySQL tests.\"\n\nMYSQL_DATABASE=next-auth\nMYSQL_ROOT_PASSWORD=password\nMYSQL_CONTAINER_NAME=next-auth-mysql-test\n\ndocker run -d --rm \\\n  -e MYSQL_DATABASE=${MYSQL_DATABASE} \\\n  -e MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} \\\n  --name \"${MYSQL_CONTAINER_NAME}\" \\\n  -p 3306:3306 \\\n  mysql:8\n\necho \"Waiting 10s for db to start...\" && sleep 10\n\ndrizzle-kit generate --config=./test/mysql-multi-project-schema/drizzle.config.ts\ndrizzle-kit migrate --config=./test/mysql-multi-project-schema/drizzle.config.ts\n\nif vitest run -c ../utils/vitest.config.ts ./test/mysql-multi-project-schema/index.test.ts; then\n  docker stop ${MYSQL_CONTAINER_NAME}\nelse\n  docker stop ${MYSQL_CONTAINER_NAME} && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-drizzle/test/pg/drizzle.config.ts",
    "content": "import type { Config } from \"drizzle-kit\"\n\nexport default {\n  schema: \"./test/pg/schema.ts\",\n  out: \"./test/pg/.drizzle\",\n  dialect: \"postgresql\",\n  dbCredentials: {\n    database: \"nextauth\",\n    host: \"127.0.0.1\",\n    user: \"nextauth\",\n    password: \"nextauth\",\n    port: 5432,\n    ssl: false,\n  },\n} satisfies Config\n"
  },
  {
    "path": "packages/adapter-drizzle/test/pg/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { DrizzleAdapter } from \"../../src\"\nimport {\n  db,\n  accountsTable as accounts,\n  sessionsTable as sessions,\n  usersTable as users,\n  verificationTokensTable as verificationTokens,\n  authenticatorsTable as authenticators,\n} from \"./schema\"\nimport { eq, and } from \"drizzle-orm\"\nimport { fixtures } from \"../fixtures\"\n\nrunBasicTests({\n  adapter: DrizzleAdapter(db),\n  fixtures,\n  testWebAuthnMethods: true,\n  db: {\n    connect: async () => {\n      await Promise.all([\n        db.delete(sessions),\n        db.delete(accounts),\n        db.delete(verificationTokens),\n        db.delete(users),\n        db.delete(authenticators),\n      ])\n    },\n    disconnect: async () => {\n      await Promise.all([\n        db.delete(sessions),\n        db.delete(accounts),\n        db.delete(verificationTokens),\n        db.delete(users),\n        db.delete(authenticators),\n      ])\n    },\n    user: (id) =>\n      db\n        .select()\n        .from(users)\n        .where(eq(users.id, id))\n        .then((res) => res[0] ?? null),\n    session: (sessionToken) =>\n      db\n        .select()\n        .from(sessions)\n        .where(eq(sessions.sessionToken, sessionToken))\n        .then((res) => res[0] ?? null),\n    account: (provider_providerAccountId) =>\n      db\n        .select()\n        .from(accounts)\n        .where(\n          eq(\n            accounts.providerAccountId,\n            provider_providerAccountId.providerAccountId\n          )\n        )\n        .then((res) => res[0] ?? null),\n    verificationToken: (identifier_token) =>\n      db\n        .select()\n        .from(verificationTokens)\n        .where(\n          and(\n            eq(verificationTokens.token, identifier_token.token),\n            eq(verificationTokens.identifier, identifier_token.identifier)\n          )\n        )\n        .then((res) => res[0]) ?? null,\n    authenticator: (credentialID) =>\n      db\n        .select()\n        .from(authenticators)\n        .where(eq(authenticators.credentialID, credentialID))\n        .then((res) => res[0]),\n  },\n})\n"
  },
  {
    "path": "packages/adapter-drizzle/test/pg/migrator.ts",
    "content": "import { migrate } from \"drizzle-orm/postgres-js/migrator\"\nimport { drizzle } from \"drizzle-orm/postgres-js\"\nimport postgres from \"postgres\"\n\nconst migrator = async () => {\n  const connectionString =\n    \"postgres://nextauth:nextauth@127.0.0.1:5432/nextauth\"\n  const sql = postgres(connectionString, { max: 1 })\n\n  await migrate(drizzle(sql), { migrationsFolder: \"./test/pg/.drizzle\" })\n}\n\nmigrator()\n  .then(() => process.exit(0))\n  .catch(() => process.exit(1))\n"
  },
  {
    "path": "packages/adapter-drizzle/test/pg/schema.ts",
    "content": "import { pgTable } from \"drizzle-orm/pg-core\"\nimport { drizzle } from \"drizzle-orm/postgres-js\"\nimport postgres from \"postgres\"\nimport { defineTables } from \"../../src/lib/pg\"\n\nexport const {\n  usersTable,\n  accountsTable,\n  sessionsTable,\n  verificationTokensTable,\n  authenticatorsTable,\n} = defineTables({})\n\nconst connectionString = \"postgres://nextauth:nextauth@127.0.0.1:5432/nextauth\"\nconst sql = postgres(connectionString)\n\nexport const db = drizzle(sql)\n"
  },
  {
    "path": "packages/adapter-drizzle/test/pg/test.sh",
    "content": "#!/usr/bin/env bash\n\necho \"Initializing container for PostgreSQL tests.\"\n\nPGUSER=nextauth\nPGPASSWORD=nextauth\nPGDATABASE=nextauth\nPGPORT=5432\nPG_CONTAINER_NAME=next-auth-postgres-test\n\ndocker run -d --rm \\\n  -e POSTGRES_USER=${PGUSER} \\\n  -e POSTGRES_PASSWORD=${PGPASSWORD} \\\n  -e POSTGRES_DB=${PGDATABASE} \\\n  -e POSTGRES_HOST_AUTH_METHOD=trust \\\n  --name \"${PG_CONTAINER_NAME}\" \\\n  -p ${PGPORT}:5432 \\\n  postgres:15.3\n\necho \"Waiting 15s for db to start...\" && sleep 15\n\n# Push schema and seed\ndrizzle-kit generate --config=./test/pg/drizzle.config.ts\ndrizzle-kit migrate --config=./test/pg/drizzle.config.ts\n\nif vitest run -c ../utils/vitest.config.ts ./test/pg/index.test.ts; then\n  docker stop ${PG_CONTAINER_NAME}\nelse\n  docker stop ${PG_CONTAINER_NAME} && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-drizzle/test/pg-multi-project-schema/drizzle.config.ts",
    "content": "import type { Config } from \"drizzle-kit\"\n\nexport default {\n  schema: \"./test/pg-multi-project-schema/schema.ts\",\n  out: \"./test/pg-multi-project-schema/.drizzle\",\n  dialect: \"postgresql\",\n  dbCredentials: {\n    database: \"nextauth\",\n    host: \"127.0.0.1\",\n    user: \"nextauth\",\n    password: \"nextauth\",\n    port: 5432,\n    ssl: false,\n  },\n} satisfies Config\n"
  },
  {
    "path": "packages/adapter-drizzle/test/pg-multi-project-schema/index.test.ts",
    "content": "import { and, eq } from \"drizzle-orm\"\nimport { runBasicTests } from \"utils/adapter\"\nimport { DrizzleAdapter } from \"../../src\"\nimport { fixtures } from \"../fixtures\"\nimport {\n  accounts,\n  authenticators,\n  db,\n  sessions,\n  users,\n  verificationTokens,\n} from \"./schema\"\n\nrunBasicTests({\n  adapter: DrizzleAdapter(db, {\n    usersTable: users,\n    accountsTable: accounts,\n    sessionsTable: sessions,\n    verificationTokensTable: verificationTokens,\n    authenticatorsTable: authenticators,\n  }),\n  fixtures,\n  testWebAuthnMethods: true,\n  db: {\n    connect: async () => {\n      await Promise.all([\n        db.delete(sessions),\n        db.delete(accounts),\n        db.delete(verificationTokens),\n        db.delete(users),\n        db.delete(authenticators),\n      ])\n    },\n    disconnect: async () => {\n      await Promise.all([\n        db.delete(sessions),\n        db.delete(accounts),\n        db.delete(verificationTokens),\n        db.delete(users),\n        db.delete(authenticators),\n      ])\n    },\n    user: (id) =>\n      db\n        .select()\n        .from(users)\n        .where(eq(users.id, id))\n        .then((res) => res[0] ?? null),\n    session: (sessionToken) =>\n      db\n        .select()\n        .from(sessions)\n        .where(eq(sessions.sessionToken, sessionToken))\n        .then((res) => res[0] ?? null),\n    account: (provider_providerAccountId) =>\n      db\n        .select()\n        .from(accounts)\n        .where(\n          eq(\n            accounts.providerAccountId,\n            provider_providerAccountId.providerAccountId\n          )\n        )\n        .then((res) => res[0] ?? null),\n    verificationToken: (identifier_token) =>\n      db\n        .select()\n        .from(verificationTokens)\n        .where(\n          and(\n            eq(verificationTokens.token, identifier_token.token),\n            eq(verificationTokens.identifier, identifier_token.identifier)\n          )\n        )\n        .then((res) => res[0]) ?? null,\n    authenticator: (credentialID) =>\n      db\n        .select()\n        .from(authenticators)\n        .where(eq(authenticators.credentialID, credentialID))\n        .then((res) => res[0]),\n  },\n})\n"
  },
  {
    "path": "packages/adapter-drizzle/test/pg-multi-project-schema/migrator.ts",
    "content": "import { migrate } from \"drizzle-orm/postgres-js/migrator\"\nimport { db } from \"./schema\"\n\nconst migrator = async () => {\n  await migrate(db, {\n    migrationsFolder: \"./test/pg-multi-project-schema/.drizzle\",\n  })\n}\n\nmigrator()\n  .then(() => process.exit(0))\n  .catch(() => process.exit(1))\n"
  },
  {
    "path": "packages/adapter-drizzle/test/pg-multi-project-schema/schema.ts",
    "content": "import type { AdapterAccountType } from \"@auth/core/adapters\"\nimport {\n  boolean,\n  integer,\n  pgTableCreator,\n  primaryKey,\n  text,\n  timestamp,\n} from \"drizzle-orm/pg-core\"\nimport { drizzle } from \"drizzle-orm/postgres-js\"\nimport postgres from \"postgres\"\nconst connectionString = \"postgres://nextauth:nextauth@localhost:5432/nextauth\"\nconst sql = postgres(connectionString)\n\nexport const db = drizzle(sql)\n\nconst pgTable = pgTableCreator((name) => `project1_${name}`)\n\nexport const users = pgTable(\"user\", {\n  id: text(\"id\")\n    .primaryKey()\n    .$defaultFn(() => crypto.randomUUID()),\n  name: text(\"name\"),\n  email: text(\"email\").unique(),\n  emailVerified: timestamp(\"emailVerified\", { mode: \"date\" }),\n  image: text(\"image\"),\n})\n\nexport const accounts = pgTable(\n  \"account\",\n  {\n    userId: text(\"userId\")\n      .notNull()\n      .references(() => users.id, { onDelete: \"cascade\" }),\n    type: text(\"type\").$type<AdapterAccountType>().notNull(),\n    provider: text(\"provider\").notNull(),\n    providerAccountId: text(\"providerAccountId\").notNull(),\n    refresh_token: text(\"refresh_token\"),\n    access_token: text(\"access_token\"),\n    expires_at: integer(\"expires_at\"),\n    token_type: text(\"token_type\"),\n    scope: text(\"scope\"),\n    id_token: text(\"id_token\"),\n    session_state: text(\"session_state\"),\n  },\n  (table) => {\n    return {\n      compositePk: primaryKey({\n        columns: [table.provider, table.providerAccountId],\n      }),\n    }\n  }\n)\n\nexport const sessions = pgTable(\"session\", {\n  sessionToken: text(\"sessionToken\").primaryKey(),\n  userId: text(\"userId\")\n    .notNull()\n    .references(() => users.id, { onDelete: \"cascade\" }),\n  expires: timestamp(\"expires\", { mode: \"date\" }).notNull(),\n})\n\nexport const verificationTokens = pgTable(\n  \"verificationToken\",\n  {\n    identifier: text(\"identifier\").notNull(),\n    token: text(\"token\").notNull(),\n    expires: timestamp(\"expires\", { mode: \"date\" }).notNull(),\n  },\n  (table) => {\n    return {\n      compositePk: primaryKey({ columns: [table.identifier, table.token] }),\n    }\n  }\n)\n\nexport const authenticators = pgTable(\n  \"authenticator\",\n  {\n    credentialID: text(\"credentialID\").notNull().unique(),\n    userId: text(\"userId\")\n      .notNull()\n      .references(() => users.id, { onDelete: \"cascade\" }),\n    providerAccountId: text(\"providerAccountId\").notNull(),\n    credentialPublicKey: text(\"credentialPublicKey\").notNull(),\n    counter: integer(\"counter\").notNull(),\n    credentialDeviceType: text(\"credentialDeviceType\").notNull(),\n    credentialBackedUp: boolean(\"credentialBackedUp\").notNull(),\n    transports: text(\"transports\"),\n  },\n  (authenticator) => ({\n    compositePk: primaryKey({\n      columns: [authenticator.userId, authenticator.credentialID],\n    }),\n  })\n)\n"
  },
  {
    "path": "packages/adapter-drizzle/test/pg-multi-project-schema/test.sh",
    "content": "#!/usr/bin/env bash\n\necho \"Initializing container for PostgreSQL tests.\"\n\nPGUSER=nextauth\nPGPASSWORD=nextauth\nPGDATABASE=nextauth\nPGPORT=5432\nPG_CONTAINER_NAME=next-auth-postgres-test\n\ndocker run -d --rm \\\n  -e POSTGRES_USER=${PGUSER} \\\n  -e POSTGRES_PASSWORD=${PGPASSWORD} \\\n  -e POSTGRES_DB=${PGDATABASE} \\\n  -e POSTGRES_HOST_AUTH_METHOD=trust \\\n  --name \"${PG_CONTAINER_NAME}\" \\\n  -p ${PGPORT}:5432 \\\n  postgres:15.3\n\necho \"Waiting 5s for db to start...\" && sleep 5\n\ndrizzle-kit generate --config=./test/pg-multi-project-schema/drizzle.config.ts\ndrizzle-kit migrate --config=./test/pg-multi-project-schema/drizzle.config.ts\n\nif vitest run -c ../utils/vitest.config.ts ./test/pg-multi-project-schema/index.test.ts; then\n  docker stop ${PG_CONTAINER_NAME}\nelse\n  docker stop ${PG_CONTAINER_NAME} && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-drizzle/test/sqlite/drizzle.config.ts",
    "content": "import type { Config } from \"drizzle-kit\"\n\nexport default {\n  schema: \"./test/sqlite/schema.ts\",\n  out: \"./test/sqlite/.drizzle\",\n  dialect: \"sqlite\",\n  dbCredentials: {\n    url: \"./db.sqlite\",\n  },\n} satisfies Config\n"
  },
  {
    "path": "packages/adapter-drizzle/test/sqlite/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { DrizzleAdapter } from \"../../src\"\nimport {\n  db,\n  accountsTable as accounts,\n  sessionsTable as sessions,\n  usersTable as users,\n  verificationTokensTable as verificationTokens,\n  authenticatorsTable as authenticators,\n} from \"./schema\"\nimport { eq, and } from \"drizzle-orm\"\nimport { fixtures } from \"../fixtures\"\n\nrunBasicTests({\n  adapter: DrizzleAdapter(db),\n  fixtures,\n  testWebAuthnMethods: true,\n  db: {\n    connect: async () => {\n      await Promise.all([\n        db.delete(sessions),\n        db.delete(accounts),\n        db.delete(verificationTokens),\n        db.delete(users),\n        db.delete(authenticators),\n      ])\n    },\n    disconnect: async () => {\n      await Promise.all([\n        db.delete(sessions),\n        db.delete(accounts),\n        db.delete(verificationTokens),\n        db.delete(users),\n        db.delete(authenticators),\n      ])\n    },\n    user: (id) => db.select().from(users).where(eq(users.id, id)).get() ?? null,\n    session: (sessionToken) =>\n      db\n        .select()\n        .from(sessions)\n        .where(eq(sessions.sessionToken, sessionToken))\n        .get() ?? null,\n    account: (provider_providerAccountId) =>\n      db\n        .select()\n        .from(accounts)\n        .where(\n          eq(\n            accounts.providerAccountId,\n            provider_providerAccountId.providerAccountId\n          )\n        )\n        .get() ?? null,\n    verificationToken: (identifier_token) =>\n      db\n        .select()\n        .from(verificationTokens)\n        .where(\n          and(\n            eq(verificationTokens.token, identifier_token.token),\n            eq(verificationTokens.identifier, identifier_token.identifier)\n          )\n        )\n        .get() ?? null,\n    authenticator: (credentialID) =>\n      db\n        .select()\n        .from(authenticators)\n        .where(eq(authenticators.credentialID, credentialID))\n        .get() ?? null,\n  },\n})\n"
  },
  {
    "path": "packages/adapter-drizzle/test/sqlite/migrator.ts",
    "content": "import { migrate } from \"drizzle-orm/better-sqlite3/migrator\"\nimport { db } from \"./schema.ts\"\n\ntry {\n  migrate(db, { migrationsFolder: \"./test/sqlite/.drizzle\" })\n  process.exit(0)\n} catch (e) {\n  process.exit(1)\n}\n"
  },
  {
    "path": "packages/adapter-drizzle/test/sqlite/schema.ts",
    "content": "import { drizzle } from \"drizzle-orm/better-sqlite3\"\nimport Database from \"libsql\"\nimport { defineTables } from \"../../src/lib/sqlite.ts\"\n\nexport const {\n  usersTable,\n  accountsTable,\n  sessionsTable,\n  verificationTokensTable,\n  authenticatorsTable,\n} = defineTables({})\n\nconst sqlite = new Database(\"db.sqlite\")\n\nexport const db = drizzle(sqlite)\n"
  },
  {
    "path": "packages/adapter-drizzle/test/sqlite/test.sh",
    "content": "#!/usr/bin/env bash\n\nset -eu\n\necho \"Running SQLite tests.\"\n\nrm -f db.sqlite\n\ndrizzle-kit generate --config=./test/sqlite/drizzle.config.ts\ndrizzle-kit migrate --config=./test/sqlite/drizzle.config.ts\n\nif vitest run -c ../utils/vitest.config.ts ./test/sqlite/index.test.ts; then\n  rm -f db.sqlite\nelse\n  rm -f db.sqlite && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-drizzle/test/sqlite-multi-project-schema/drizzle.config.ts",
    "content": "import type { Config } from \"drizzle-kit\"\n\nexport default {\n  schema: \"./test/sqlite-multi-project-schema/schema.ts\",\n  out: \"./test/sqlite-multi-project-schema/.drizzle\",\n  dialect: \"sqlite\",\n  dbCredentials: {\n    url: \"./db.sqlite\",\n  },\n} satisfies Config\n"
  },
  {
    "path": "packages/adapter-drizzle/test/sqlite-multi-project-schema/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { DrizzleAdapter } from \"../../src\"\nimport {\n  db,\n  accounts,\n  sessions,\n  users,\n  verificationTokens,\n  authenticators,\n} from \"./schema\"\nimport { eq, and } from \"drizzle-orm\"\nimport { fixtures } from \"../fixtures\"\n\nrunBasicTests({\n  adapter: DrizzleAdapter(db, {\n    usersTable: users,\n    accountsTable: accounts,\n    sessionsTable: sessions,\n    verificationTokensTable: verificationTokens,\n    authenticatorsTable: authenticators,\n  }),\n  fixtures,\n  db: {\n    connect: async () => {\n      await Promise.all([\n        db.delete(sessions),\n        db.delete(accounts),\n        db.delete(verificationTokens),\n        db.delete(users),\n        db.delete(authenticators),\n      ])\n    },\n    disconnect: async () => {\n      await Promise.all([\n        db.delete(sessions),\n        db.delete(accounts),\n        db.delete(verificationTokens),\n        db.delete(users),\n        db.delete(authenticators),\n      ])\n    },\n    user: (id) => db.select().from(users).where(eq(users.id, id)).get() ?? null,\n    session: (sessionToken) =>\n      db\n        .select()\n        .from(sessions)\n        .where(eq(sessions.sessionToken, sessionToken))\n        .get() ?? null,\n    account: (provider_providerAccountId) =>\n      db\n        .select()\n        .from(accounts)\n        .where(\n          eq(\n            accounts.providerAccountId,\n            provider_providerAccountId.providerAccountId\n          )\n        )\n        .get() ?? null,\n    verificationToken: (identifier_token) =>\n      db\n        .select()\n        .from(verificationTokens)\n        .where(\n          and(\n            eq(verificationTokens.token, identifier_token.token),\n            eq(verificationTokens.identifier, identifier_token.identifier)\n          )\n        )\n        .get() ?? null,\n    authenticator: (credentialID) =>\n      db\n        .select()\n        .from(authenticators)\n        .where(eq(authenticators.credentialID, credentialID))\n        .get() ?? null,\n  },\n})\n"
  },
  {
    "path": "packages/adapter-drizzle/test/sqlite-multi-project-schema/migrator.ts",
    "content": "import { migrate } from \"drizzle-orm/better-sqlite3/migrator\"\nimport { db } from \"./schema.ts\"\n\ntry {\n  migrate(db, {\n    migrationsFolder: \"./test/sqlite-multi-project-schema/.drizzle\",\n  })\n  process.exit(0)\n} catch (e) {\n  process.exit(1)\n}\n"
  },
  {
    "path": "packages/adapter-drizzle/test/sqlite-multi-project-schema/schema.ts",
    "content": "import { AdapterAccountType } from \"@auth/core/adapters\"\nimport { drizzle } from \"drizzle-orm/better-sqlite3\"\nimport Database from \"libsql\"\nimport {\n  integer,\n  primaryKey,\n  sqliteTableCreator,\n  text,\n} from \"drizzle-orm/sqlite-core\"\n\nconst sqlite = new Database(\"db.sqlite\")\n\nexport const db = drizzle(sqlite)\n\nconst sqliteTable = sqliteTableCreator((name) => `foobar_${name}`)\n\nexport const users = sqliteTable(\"user\", {\n  id: text(\"id\")\n    .primaryKey()\n    .$defaultFn(() => crypto.randomUUID()),\n  name: text(\"name\"),\n  email: text(\"email\").unique(),\n  emailVerified: integer(\"emailVerified\", { mode: \"timestamp_ms\" }),\n  image: text(\"image\"),\n})\n\nexport const accounts = sqliteTable(\n  \"account\",\n  {\n    userId: text(\"userId\")\n      .notNull()\n      .references(() => users.id, { onDelete: \"cascade\" }),\n    type: text(\"type\").$type<AdapterAccountType>().notNull(),\n    provider: text(\"provider\").notNull(),\n    providerAccountId: text(\"providerAccountId\").notNull(),\n    refresh_token: text(\"refresh_token\"),\n    access_token: text(\"access_token\"),\n    expires_at: integer(\"expires_at\"),\n    token_type: text(\"token_type\"),\n    scope: text(\"scope\"),\n    id_token: text(\"id_token\"),\n    session_state: text(\"session_state\"),\n  },\n  (account) => ({\n    compositePk: primaryKey({\n      columns: [account.provider, account.providerAccountId],\n    }),\n  })\n)\n\nexport const sessions = sqliteTable(\"session\", {\n  sessionToken: text(\"sessionToken\").primaryKey(),\n  userId: text(\"userId\")\n    .notNull()\n    .references(() => users.id, { onDelete: \"cascade\" }),\n  expires: integer(\"expires\", { mode: \"timestamp_ms\" }).notNull(),\n})\n\nexport const verificationTokens = sqliteTable(\n  \"verificationToken\",\n  {\n    identifier: text(\"identifier\").notNull(),\n    token: text(\"token\").notNull(),\n    expires: integer(\"expires\", { mode: \"timestamp_ms\" }).notNull(),\n  },\n  (vt) => ({\n    compositePk: primaryKey({ columns: [vt.identifier, vt.token] }),\n  })\n)\n\nexport const authenticators = sqliteTable(\n  \"authenticator\",\n  {\n    credentialID: text(\"credentialID\").notNull().unique(),\n    userId: text(\"userId\")\n      .notNull()\n      .references(() => users.id, { onDelete: \"cascade\" }),\n    providerAccountId: text(\"providerAccountId\").notNull(),\n    credentialPublicKey: text(\"credentialPublicKey\").notNull(),\n    counter: integer(\"counter\").notNull(),\n    credentialDeviceType: text(\"credentialDeviceType\").notNull(),\n    credentialBackedUp: integer(\"credentialBackedUp\", {\n      mode: \"boolean\",\n    }).notNull(),\n    transports: text(\"transports\"),\n  },\n  (authenticator) => ({\n    compositePk: primaryKey({\n      columns: [authenticator.userId, authenticator.credentialID],\n    }),\n  })\n)\n"
  },
  {
    "path": "packages/adapter-drizzle/test/sqlite-multi-project-schema/test.sh",
    "content": "#!/usr/bin/env bash\n\nset -eu\n\necho \"Running SQLite tests.\"\n\nrm -f db.sqlite\n\ndrizzle-kit generate --config=./test/sqlite-multi-project-schema/drizzle.config.ts\ndrizzle-kit migrate --config=./test/sqlite-multi-project-schema/drizzle.config.ts\n\nif vitest run -c ../utils/vitest.config.ts ./test/sqlite-multi-project-schema/index.test.ts; then\n  rm -f db.sqlite\nelse\n  rm -f db.sqlite && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-drizzle/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-drizzle/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\", \"src/lib/mysql.ts\", \"src/lib/pg.ts\", \"src/lib/sqlite.ts\", \"src/lib/utils.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/drizzle-adapter\",\n  entryFileName: \"../drizzle-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-dynamodb/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://aws.amazon.com/dynamodb\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/dynamodb.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>DynamoDB Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/dynamodb-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/dynamodb-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/dynamodb-adapter?color=green&label=@auth/dynamodb-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/dynamodb-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/dynamodb-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/dynamodb).\n"
  },
  {
    "path": "packages/adapter-dynamodb/jest-dynamodb-config.cjs",
    "content": "module.exports = {\n  tables: [\n    {\n      TableName: `next-auth`,\n      KeySchema: [\n        { AttributeName: \"pk\", KeyType: \"HASH\" },\n        { AttributeName: \"sk\", KeyType: \"RANGE\" },\n      ],\n      AttributeDefinitions: [\n        { AttributeName: \"sk\", AttributeType: \"S\" },\n        { AttributeName: \"pk\", AttributeType: \"S\" },\n        { AttributeName: \"GSI1PK\", AttributeType: \"S\" },\n        { AttributeName: \"GSI1SK\", AttributeType: \"S\" },\n      ],\n      GlobalSecondaryIndexes: [\n        {\n          IndexName: \"GSI1\",\n          KeySchema: [\n            { AttributeName: \"GSI1PK\", KeyType: \"HASH\" },\n            { AttributeName: \"GSI1SK\", KeyType: \"RANGE\" },\n          ],\n          Projection: {\n            ProjectionType: \"ALL\",\n          },\n          ProvisionedThroughput: {\n            ReadCapacityUnits: 1,\n            WriteCapacityUnits: 1,\n          },\n        },\n      ],\n      ProvisionedThroughput: { ReadCapacityUnits: 1, WriteCapacityUnits: 1 },\n    },\n    // etc\n  ],\n  port: 8000,\n  installerConfig: {\n    installPath: \"./dynamodblocal-bin\",\n    downloadUrl:\n      \"https://s3.eu-central-1.amazonaws.com/dynamodb-local-frankfurt/dynamodb_local_latest.tar.gz\",\n  },\n}\n"
  },
  {
    "path": "packages/adapter-dynamodb/package.json",
    "content": "{\n  \"name\": \"@auth/dynamodb-adapter\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"version\": \"2.11.0\",\n  \"description\": \"AWS DynamoDB adapter for next-auth.\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"dynamodb\"\n  ],\n  \"homepage\": \"https://authjs.dev\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"test:default\": \"vitest -c ../utils/vitest.config.ts\",\n    \"test:custom\": \"CUSTOM_MODEL=1 vitest -c ../utils/vitest.config.ts\",\n    \"test\": \"pnpm test:default && pnpm test:custom\",\n    \"clean\": \"rm -rf index.*\",\n    \"build\": \"pnpm clean && tsc\"\n  },\n  \"author\": \"Pol Marnette\",\n  \"contributors\": [\n    \"Balázs Orbán <info@balazsorban.com>\"\n  ],\n  \"license\": \"ISC\",\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"peerDependencies\": {\n    \"@aws-sdk/client-dynamodb\": \"^3.36.1\",\n    \"@aws-sdk/lib-dynamodb\": \"^3.36.1\"\n  },\n  \"devDependencies\": {\n    \"@aws-sdk/client-dynamodb\": \"^3.36.1\",\n    \"@aws-sdk/lib-dynamodb\": \"^3.36.1\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-dynamodb/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: \"16px\"}}>\n *  <p>Official <a href=\"https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html\">DynamoDB</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://docs.aws.amazon.com/dynamodb/index.html\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/dynamodb.png\" width=\"48\"/>\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install next-auth @auth/dynamodb-adapter\n * ```\n *\n * @module @auth/dynamodb-adapter\n */\n\nimport type {\n  BatchWriteCommandInput,\n  DynamoDBDocument,\n} from \"@aws-sdk/lib-dynamodb\"\nimport {\n  type Adapter,\n  type AdapterSession,\n  type AdapterAccount,\n  type AdapterUser,\n  type VerificationToken,\n  isDate,\n} from \"@auth/core/adapters\"\n\nexport interface DynamoDBAdapterOptions {\n  tableName?: string\n  partitionKey?: string\n  sortKey?: string\n  indexName?: string\n  indexPartitionKey?: string\n  indexSortKey?: string\n}\n\nexport function DynamoDBAdapter(\n  client: DynamoDBDocument,\n  options?: DynamoDBAdapterOptions\n): Adapter {\n  const TableName = options?.tableName ?? \"next-auth\"\n  const pk = options?.partitionKey ?? \"pk\"\n  const sk = options?.sortKey ?? \"sk\"\n  const IndexName = options?.indexName ?? \"GSI1\"\n  const GSI1PK = options?.indexPartitionKey ?? \"GSI1PK\"\n  const GSI1SK = options?.indexSortKey ?? \"GSI1SK\"\n\n  return {\n    async createUser(data) {\n      const user: AdapterUser = {\n        ...(data as any),\n        id: crypto.randomUUID(),\n      }\n\n      await client.put({\n        TableName,\n        Item: format.to({\n          ...user,\n          [pk]: `USER#${user.id}`,\n          [sk]: `USER#${user.id}`,\n          type: \"USER\",\n          [GSI1PK]: `USER#${user.email}`,\n          [GSI1SK]: `USER#${user.email}`,\n        }),\n      })\n\n      return user\n    },\n    async getUser(userId) {\n      const data = await client.get({\n        TableName,\n        Key: {\n          [pk]: `USER#${userId}`,\n          [sk]: `USER#${userId}`,\n        },\n      })\n      return format.from<AdapterUser>(data.Item)\n    },\n    async getUserByEmail(email) {\n      const data = await client.query({\n        TableName,\n        IndexName,\n        KeyConditionExpression: \"#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk\",\n        ExpressionAttributeNames: {\n          \"#gsi1pk\": GSI1PK,\n          \"#gsi1sk\": GSI1SK,\n        },\n        ExpressionAttributeValues: {\n          \":gsi1pk\": `USER#${email}`,\n          \":gsi1sk\": `USER#${email}`,\n        },\n      })\n\n      return format.from<AdapterUser>(data.Items?.[0])\n    },\n    async getUserByAccount({ provider, providerAccountId }) {\n      const data = await client.query({\n        TableName,\n        IndexName,\n        KeyConditionExpression: \"#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk\",\n        ExpressionAttributeNames: {\n          \"#gsi1pk\": GSI1PK,\n          \"#gsi1sk\": GSI1SK,\n        },\n        ExpressionAttributeValues: {\n          \":gsi1pk\": `ACCOUNT#${provider}`,\n          \":gsi1sk\": `ACCOUNT#${providerAccountId}`,\n        },\n      })\n      if (!data.Items?.length) return null\n\n      const accounts = data.Items[0] as AdapterAccount\n      const res = await client.get({\n        TableName,\n        Key: {\n          [pk]: `USER#${accounts.userId}`,\n          [sk]: `USER#${accounts.userId}`,\n        },\n      })\n      return format.from<AdapterUser>(res.Item)\n    },\n    async updateUser(user) {\n      const {\n        UpdateExpression,\n        ExpressionAttributeNames,\n        ExpressionAttributeValues,\n      } = generateUpdateExpression(user)\n      const data = await client.update({\n        TableName,\n        Key: {\n          [pk]: `USER#${user.id}`,\n          [sk]: `USER#${user.id}`,\n        },\n        UpdateExpression,\n        ExpressionAttributeNames,\n        ExpressionAttributeValues,\n        ReturnValues: \"ALL_NEW\",\n      })\n\n      return format.from<AdapterUser>(data.Attributes)!\n    },\n    async deleteUser(userId) {\n      // query all the items related to the user to delete\n      const res = await client.query({\n        TableName,\n        KeyConditionExpression: \"#pk = :pk\",\n        ExpressionAttributeNames: { \"#pk\": pk },\n        ExpressionAttributeValues: { \":pk\": `USER#${userId}` },\n      })\n      if (!res.Items) return null\n      const items = res.Items\n      // find the user we want to delete to return at the end of the function call\n      const user = items.find((item) => item.type === \"USER\")\n      const itemsToDelete = items.map((item) => {\n        return {\n          DeleteRequest: {\n            Key: {\n              [sk]: item.sk,\n              [pk]: item.pk,\n            },\n          },\n        }\n      })\n      // batch write commands cannot handle more than 25 requests at once\n      const itemsToDeleteMax = itemsToDelete.slice(0, 25)\n      const param: BatchWriteCommandInput = {\n        RequestItems: { [TableName]: itemsToDeleteMax },\n      }\n      await client.batchWrite(param)\n      return format.from<AdapterUser>(user)\n    },\n    async linkAccount(data) {\n      const item = {\n        ...data,\n        id: crypto.randomUUID(),\n        [pk]: `USER#${data.userId}`,\n        [sk]: `ACCOUNT#${data.provider}#${data.providerAccountId}`,\n        [GSI1PK]: `ACCOUNT#${data.provider}`,\n        [GSI1SK]: `ACCOUNT#${data.providerAccountId}`,\n      }\n      await client.put({ TableName, Item: format.to(item) })\n      return data\n    },\n    async unlinkAccount({ provider, providerAccountId }) {\n      const data = await client.query({\n        TableName,\n        IndexName,\n        KeyConditionExpression: \"#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk\",\n        ExpressionAttributeNames: {\n          \"#gsi1pk\": GSI1PK,\n          \"#gsi1sk\": GSI1SK,\n        },\n        ExpressionAttributeValues: {\n          \":gsi1pk\": `ACCOUNT#${provider}`,\n          \":gsi1sk\": `ACCOUNT#${providerAccountId}`,\n        },\n      })\n      const account = format.from<AdapterAccount>(data.Items?.[0])\n      if (!account) return\n      await client.delete({\n        TableName,\n        Key: {\n          [pk]: `USER#${account.userId}`,\n          [sk]: `ACCOUNT#${provider}#${providerAccountId}`,\n        },\n        ReturnValues: \"ALL_OLD\",\n      })\n      return account\n    },\n    async getSessionAndUser(sessionToken) {\n      const data = await client.query({\n        TableName,\n        IndexName,\n        KeyConditionExpression: \"#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk\",\n        ExpressionAttributeNames: {\n          \"#gsi1pk\": GSI1PK,\n          \"#gsi1sk\": GSI1SK,\n        },\n        ExpressionAttributeValues: {\n          \":gsi1pk\": `SESSION#${sessionToken}`,\n          \":gsi1sk\": `SESSION#${sessionToken}`,\n        },\n      })\n      const session = format.from<AdapterSession>(data.Items?.[0])\n      if (!session) return null\n      const res = await client.get({\n        TableName,\n        Key: {\n          [pk]: `USER#${session.userId}`,\n          [sk]: `USER#${session.userId}`,\n        },\n      })\n      const user = format.from<AdapterUser>(res.Item)\n      if (!user) return null\n      return { user, session }\n    },\n    async createSession(data) {\n      const session = {\n        id: crypto.randomUUID(),\n        ...data,\n      }\n      await client.put({\n        TableName,\n        Item: format.to({\n          [pk]: `USER#${data.userId}`,\n          [sk]: `SESSION#${data.sessionToken}`,\n          [GSI1SK]: `SESSION#${data.sessionToken}`,\n          [GSI1PK]: `SESSION#${data.sessionToken}`,\n          type: \"SESSION\",\n          ...data,\n        }),\n      })\n      return session\n    },\n    async updateSession(session) {\n      const { sessionToken } = session\n      const data = await client.query({\n        TableName,\n        IndexName,\n        KeyConditionExpression: \"#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk\",\n        ExpressionAttributeNames: {\n          \"#gsi1pk\": GSI1PK,\n          \"#gsi1sk\": GSI1SK,\n        },\n        ExpressionAttributeValues: {\n          \":gsi1pk\": `SESSION#${sessionToken}`,\n          \":gsi1sk\": `SESSION#${sessionToken}`,\n        },\n      })\n      if (!data.Items?.length) return null\n      const sessionRecord = data.Items[0]\n      const {\n        UpdateExpression,\n        ExpressionAttributeNames,\n        ExpressionAttributeValues,\n      } = generateUpdateExpression(session)\n      const res = await client.update({\n        TableName,\n        Key: {\n          [pk]: sessionRecord[pk],\n          [sk]: sessionRecord[sk],\n        },\n        UpdateExpression,\n        ExpressionAttributeNames,\n        ExpressionAttributeValues,\n        ReturnValues: \"ALL_NEW\",\n      })\n      return format.from<AdapterSession>(res.Attributes)\n    },\n    async deleteSession(sessionToken) {\n      const data = await client.query({\n        TableName,\n        IndexName,\n        KeyConditionExpression: \"#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk\",\n        ExpressionAttributeNames: {\n          \"#gsi1pk\": GSI1PK,\n          \"#gsi1sk\": GSI1SK,\n        },\n        ExpressionAttributeValues: {\n          \":gsi1pk\": `SESSION#${sessionToken}`,\n          \":gsi1sk\": `SESSION#${sessionToken}`,\n        },\n      })\n      if (!data?.Items?.length) return null\n\n      const sessionRecord = data.Items[0]\n\n      const res = await client.delete({\n        TableName,\n        Key: {\n          [pk]: sessionRecord[pk],\n          [sk]: sessionRecord[sk],\n        },\n        ReturnValues: \"ALL_OLD\",\n      })\n      return format.from<AdapterSession>(res.Attributes)\n    },\n    async createVerificationToken(data) {\n      await client.put({\n        TableName,\n        Item: format.to({\n          [pk]: `VT#${data.identifier}`,\n          [sk]: `VT#${data.token}`,\n          type: \"VT\",\n          ...data,\n        }),\n      })\n      return data\n    },\n    async useVerificationToken({ identifier, token }) {\n      const data = await client.delete({\n        TableName,\n        Key: {\n          [pk]: `VT#${identifier}`,\n          [sk]: `VT#${token}`,\n        },\n        ReturnValues: \"ALL_OLD\",\n      })\n      return format.from<VerificationToken>(data.Attributes)\n    },\n  }\n}\n\nconst format = {\n  /** Takes a plain old JavaScript object and turns it into a DynamoDB object */\n  to(object: Record<string, any>) {\n    const newObject: Record<string, unknown> = {}\n    for (const key in object) {\n      const value = object[key]\n      if (value instanceof Date) {\n        // DynamoDB requires the TTL attribute be a UNIX timestamp (in secs).\n        if (key === \"expires\") newObject[key] = value.getTime() / 1000\n        else newObject[key] = value.toISOString()\n      } else newObject[key] = value\n    }\n    return newObject\n  },\n  /** Takes a Dynamo object and returns a plain old JavaScript object */\n  from<T = Record<string, unknown>>(object?: Record<string, any>): T | null {\n    if (!object) return null\n    const newObject: Record<string, unknown> = {}\n    for (const key in object) {\n      // Filter DynamoDB specific attributes so it doesn't get passed to core,\n      // to avoid revealing the type of database\n      if ([\"pk\", \"sk\", \"GSI1PK\", \"GSI1SK\"].includes(key)) continue\n\n      const value = object[key]\n\n      if (isDate(value)) newObject[key] = new Date(value)\n      // hack to keep type property in account\n      else if (key === \"type\" && [\"SESSION\", \"VT\", \"USER\"].includes(value))\n        continue\n      // The expires property is stored as a UNIX timestamp in seconds, but\n      // JavaScript needs it in milliseconds, so multiply by 1000.\n      else if (key === \"expires\" && typeof value === \"number\")\n        newObject[key] = new Date(value * 1000)\n      else newObject[key] = value\n    }\n    return newObject as T\n  },\n}\n\nfunction generateUpdateExpression(object: Record<string, any>): {\n  UpdateExpression: string\n  ExpressionAttributeNames: Record<string, string>\n  ExpressionAttributeValues: Record<string, unknown>\n} {\n  const formattedSession = format.to(object)\n  let UpdateExpression = \"set\"\n  const ExpressionAttributeNames: Record<string, string> = {}\n  const ExpressionAttributeValues: Record<string, unknown> = {}\n  for (const property in formattedSession) {\n    UpdateExpression += ` #${property} = :${property},`\n    ExpressionAttributeNames[\"#\" + property] = property\n    ExpressionAttributeValues[\":\" + property] = formattedSession[property]\n  }\n  UpdateExpression = UpdateExpression.slice(0, -1)\n  return {\n    UpdateExpression,\n    ExpressionAttributeNames,\n    ExpressionAttributeValues,\n  }\n}\n\nexport { format, generateUpdateExpression }\n"
  },
  {
    "path": "packages/adapter-dynamodb/test/format.test.ts",
    "content": "import { describe, expect, test } from \"vitest\"\nimport { format } from \"../src\"\n\ndescribe(\"dynamodb utils.format\", () => {\n  test(\"format.to() preserves non-Date non-expires properties\", () => {\n    expect(\n      format.to({\n        pk: \"test-pk\",\n        email: \"test@example.com\",\n      })\n    ).toEqual({\n      pk: \"test-pk\",\n      email: \"test@example.com\",\n    })\n  })\n\n  test(\"format.to() converts non-expires Date properties to ISO strings\", () => {\n    const date = new Date()\n    expect(\n      format.to({\n        dateProp: date,\n      })\n    ).toEqual({\n      dateProp: date.toISOString(),\n    })\n  })\n\n  test(\"format.to() converts expires property to a UNIX timestamp\", () => {\n    // DynamoDB requires that the property used for TTL is a UNIX timestamp.\n    const date = new Date()\n    const timestamp = date.getTime() / 1000\n    expect(\n      format.to({\n        expires: date,\n      })\n    ).toEqual({\n      expires: timestamp,\n    })\n  })\n\n  test(\"format.from() preserves non-special attributes\", () => {\n    expect(\n      format.from({\n        testAttr1: \"test-value\",\n        testAttr2: 5,\n      })\n    ).toEqual({\n      testAttr1: \"test-value\",\n      testAttr2: 5,\n    })\n  })\n\n  test(\"format.from() removes dynamodb key attributes\", () => {\n    expect(\n      format.from({\n        pk: \"test-pk\",\n        sk: \"test-sk\",\n        GSI1PK: \"test-GSI1PK\",\n        GSI1SK: \"test-GSI1SK\",\n      })\n    ).toEqual({})\n  })\n\n  test(\"format.from() only removes type attribute from Session, VT, and User\", () => {\n    expect(format.from({ type: \"SESSION\" })).toEqual({})\n    expect(format.from({ type: \"VT\" })).toEqual({})\n    expect(format.from({ type: \"USER\" })).toEqual({})\n    expect(format.from({ type: \"ANYTHING\" })).toEqual({ type: \"ANYTHING\" })\n    expect(format.from({ type: \"ELSE\" })).toEqual({ type: \"ELSE\" })\n  })\n\n  test(\"format.from() converts ISO strings to Date instances\", () => {\n    const date = new Date()\n    expect(\n      format.from({\n        someDate: date.toISOString(),\n      })\n    ).toEqual({\n      someDate: date,\n    })\n  })\n\n  test(\"format.from() converts expires attribute from timestamp to Date instance\", () => {\n    // AdapterSession[\"expires\"] and VerificationToken[\"expires\"] are both meant\n    // to be Date instances.\n    const date = new Date()\n    const timestamp = date.getTime() / 1000\n    expect(\n      format.from({\n        expires: timestamp,\n      })\n    ).toEqual({\n      expires: date,\n    })\n  })\n\n  test(\"format.from() converts expires attribute from ISO string to Date instance\", () => {\n    // Due to a bug in an old version, some expires attributes were stored as\n    // ISO strings, so we need to handle those properly too.\n    const date = new Date()\n    expect(\n      format.from({\n        expires: date.toISOString(),\n      })\n    ).toEqual({\n      expires: date,\n    })\n  })\n\n  test(\"format.from(format.to()) preserves expires attribute\", () => {\n    const date = new Date()\n    expect(\n      format.from(\n        format.to({\n          expires: date,\n        })\n      )\n    ).toEqual({\n      expires: date,\n    })\n  })\n})\n"
  },
  {
    "path": "packages/adapter-dynamodb/test/index.test.ts",
    "content": "import { DynamoDB } from \"@aws-sdk/client-dynamodb\"\nimport { DynamoDBDocument } from \"@aws-sdk/lib-dynamodb\"\nimport { DynamoDBAdapter, format } from \"../src\"\nimport { runBasicTests } from \"utils/adapter\"\n\nconst config = {\n  endpoint: \"http://127.0.0.1:8000\",\n  region: \"eu-central-1\",\n  tls: false,\n  credentials: {\n    accessKeyId: \"foo\",\n    secretAccessKey: \"bar\",\n  },\n}\n\nconst client = DynamoDBDocument.from(new DynamoDB(config), {\n  marshallOptions: {\n    convertEmptyValues: true,\n    removeUndefinedValues: true,\n    convertClassInstanceToMap: true,\n  },\n})\n\nconst adapter = DynamoDBAdapter(client)\n\nconst TableName = \"next-auth\"\n\nrunBasicTests({\n  adapter,\n  db: {\n    async user(id) {\n      const user = await client.get({\n        TableName,\n        Key: {\n          pk: `USER#${id}`,\n          sk: `USER#${id}`,\n        },\n      })\n\n      return format.from(user.Item)\n    },\n    async session(token) {\n      const session = await client.query({\n        TableName,\n        IndexName: \"GSI1\",\n        KeyConditionExpression: \"#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk\",\n        ExpressionAttributeNames: {\n          \"#gsi1pk\": \"GSI1PK\",\n          \"#gsi1sk\": \"GSI1SK\",\n        },\n        ExpressionAttributeValues: {\n          \":gsi1pk\": `SESSION#${token}`,\n          \":gsi1sk\": `SESSION#${token}`,\n        },\n      })\n\n      return format.from(session.Items?.[0])\n    },\n    async account({ provider, providerAccountId }) {\n      const account = await client.query({\n        TableName,\n        IndexName: \"GSI1\",\n        KeyConditionExpression: \"#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk\",\n        ExpressionAttributeNames: {\n          \"#gsi1pk\": \"GSI1PK\",\n          \"#gsi1sk\": \"GSI1SK\",\n        },\n        ExpressionAttributeValues: {\n          \":gsi1pk\": `ACCOUNT#${provider}`,\n          \":gsi1sk\": `ACCOUNT#${providerAccountId}`,\n        },\n      })\n\n      return format.from(account.Items?.[0])\n    },\n    async verificationToken({ token, identifier }) {\n      const vt = await client.get({\n        TableName,\n        Key: {\n          pk: `VT#${identifier}`,\n          sk: `VT#${token}`,\n        },\n      })\n      return format.from(vt.Item)\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-dynamodb/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-dynamodb/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/dynamodb-adapter\",\n  entryFileName: \"../dynamodb-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-edgedb/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://www.edgedb.com/\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/edgedb.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Edge DB Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/edgedb-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/edgedb-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/edgedb-adapter?color=green&label=@auth/edgedb-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/edgedb-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/edgedb-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/edgedb).\n"
  },
  {
    "path": "packages/adapter-edgedb/package.json",
    "content": "{\n  \"name\": \"@auth/edgedb-adapter\",\n  \"version\": \"1.11.0\",\n  \"description\": \"EdgeDB adapter for next-auth.\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Bruno Crosier\",\n  \"contributors\": [\n    \"Thang Huu Vu <hi@thvu.dev>\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.d.ts*\",\n    \"*.js\",\n    \"lib\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"edgedb\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"tsc\",\n    \"test\": \"vitest -c ../utils/vitest.config.ts\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"edgedb\": \"^1.0.1\"\n  },\n  \"devDependencies\": {\n    \"edgedb\": \"^1.0.1\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-edgedb/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: \"16px\"}}>\n *  <p>Official <a href=\"https://www.edgedb.com/\">Edge DB</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://www.edgedb.com/\">\n *   <img style={{display: \"block\"}} src=\"/img/adapters/edgedb.svg\" width=\"38\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install edgedb @auth/edgedb-adapter\n * npm install @edgedb/generate --save-dev\n * ```\n *\n * @module @auth/edgedb-adapter\n */\n\nimport type {\n  Adapter,\n  AdapterSession,\n  AdapterUser,\n  VerificationToken,\n} from \"@auth/core/adapters\"\nimport type { Client } from \"edgedb\"\n\nexport function EdgeDBAdapter(client: Client): Adapter {\n  return {\n    async createUser({ email, emailVerified, name, image }) {\n      return await client.queryRequiredSingle(\n        `\n        with\n          image := <optional str>$image,\n          name := <optional str>$name,\n          emailVerified := <optional str>$emailVerified\n\n        select (\n          insert User {\n            email:= <str>$email,\n            emailVerified:= <datetime>emailVerified,\n            name:= name,\n            image:= image,\n          }\n        ) {\n            id,\n            email,\n            emailVerified,\n            name,\n            image\n          }\n        `,\n        {\n          email,\n          emailVerified: emailVerified && new Date(emailVerified).toISOString(),\n          name,\n          image,\n        }\n      )\n    },\n    async getUser(id) {\n      return await client.querySingle<AdapterUser>(\n        `\n        select User {\n          id,\n          email,\n          emailVerified,\n          name,\n          image\n        } filter .id = <uuid>$id;\n        `,\n        { id }\n      )\n    },\n    async getUserByEmail(email) {\n      return await client.querySingle<AdapterUser>(\n        `\n        select User {\n          id,\n          email,\n          emailVerified,\n          name,\n          image\n        } filter .email = <str>$email;\n        `,\n        { email }\n      )\n    },\n    async getUserByAccount({ providerAccountId, provider }) {\n      return await client.querySingle<AdapterUser>(\n        `\n        with account := (\n          select Account\n          filter .providerAccountId = <str>$providerAccountId\n             and .provider = <str>$provider\n        )\n        select account.user {\n          id,\n          email,\n          image,\n          name,\n          emailVerified\n        }\n        `,\n        { providerAccountId, provider }\n      )\n    },\n    async updateUser({ email, emailVerified, id, image, name }) {\n      return await client.queryRequiredSingle<AdapterUser>(\n        `       \n        with \n          email := <optional str>$email,\n          emailVerified := <optional str>$emailVerified, \n          image := <optional str>$image,\n          name := <optional str>$name\n        \n        select (\n          update User\n          filter .id = <uuid>$id\n          set {\n            email := email ?? .email,\n            emailVerified := <datetime>emailVerified ?? .emailVerified,\n            image := image ?? .image,\n            name := name ?? .name,\n          }\n        ) {\n          id,\n          email,\n          emailVerified,\n          image,\n          name\n        }\n        `,\n        {\n          email,\n          emailVerified: emailVerified && new Date(emailVerified).toISOString(),\n          id,\n          image,\n          name,\n        }\n      )\n    },\n    async deleteUser(id) {\n      await client.execute(`delete User filter .id = <uuid>$id;`, { id })\n    },\n    async linkAccount({\n      userId,\n      type,\n      provider,\n      providerAccountId,\n      refresh_token,\n      access_token,\n      expires_at,\n      token_type,\n      scope,\n      id_token,\n      session_state,\n    }) {\n      await client.execute(\n        `\n        with \n          userId := <optional str>$userId,\n          refresh_token := <optional str>$refresh_token,\n          access_token := <optional str>$access_token,\n          expires_at := <optional str>$expires_at,\n          token_type := <optional str>$token_type,\n          scope := <optional str>$scope,\n          id_token := <optional str>$id_token,\n          session_state := <optional str>$session_state\n\n        insert Account {\n          type := <str>$type,\n          provider := <str>$provider,\n          providerAccountId := <str>$providerAccountId,\n          refresh_token := refresh_token,\n          access_token := access_token,\n          expires_at := <int64>expires_at,\n          token_type := token_type,\n          scope := scope,\n          id_token := id_token,\n          session_state := session_state,\n          user := (\n            select User filter .id = <uuid>userId\n          )\n        }\n        `,\n        {\n          userId,\n          type,\n          provider,\n          providerAccountId,\n          refresh_token,\n          access_token,\n          expires_at: expires_at && String(expires_at),\n          token_type,\n          scope,\n          id_token,\n          session_state,\n        }\n      )\n    },\n    async unlinkAccount({ providerAccountId, provider }) {\n      await client.execute(\n        `\n        delete Account filter \n        .providerAccountId = <str>$providerAccountId\n        and\n        .provider = <str>$provider\n        `,\n        { providerAccountId, provider }\n      )\n    },\n    async createSession({ expires, sessionToken, userId }) {\n      return await client.queryRequiredSingle<AdapterSession>(\n        `   \n        select (\n          insert Session {\n            expires := <datetime>$expires,\n            sessionToken := <str>$sessionToken,\n            user := (\n              select User filter .id = <uuid>$userId\n            )\n          }\n        ) {\n          expires,\n          sessionToken,\n          userId\n        };\n      `,\n        { expires, sessionToken, userId }\n      )\n    },\n    async getSessionAndUser(sessionToken) {\n      const sessionAndUser = await client.querySingle<\n        AdapterSession & { user: AdapterUser }\n      >(\n        `\n        select Session {\n          userId,\n          id,\n          expires,\n          sessionToken,\n          user: {\n            id,\n            email,\n            emailVerified,\n            image,\n            name\n          }\n        } filter .sessionToken = <str>$sessionToken;\n      `,\n        { sessionToken }\n      )\n\n      if (!sessionAndUser) {\n        return null\n      }\n\n      const { user, ...session } = sessionAndUser\n\n      if (!user || !session) {\n        return null\n      }\n\n      return {\n        user,\n        session,\n      }\n    },\n    async updateSession({ sessionToken, expires, userId }) {\n      return await client.querySingle<AdapterSession>(\n        `\n        with \n          sessionToken := <optional str>$sessionToken,\n          expires := <optional str>$expires, \n          userId := <optional str>$userId,\n          user := (\n            select User filter .id = <uuid>userId\n          )\n\n        select (          \n          update Session\n          filter .sessionToken = <str>$sessionToken\n          set {\n            sessionToken := sessionToken ?? .sessionToken,\n            expires := <datetime>expires ?? .expires,\n            user := assert_exists(user ?? .user)\n          }\n        ) {\n          sessionToken,\n          userId,\n          expires\n        }\n      `,\n        {\n          sessionToken,\n          expires: expires && new Date(expires).toISOString(),\n          userId,\n        }\n      )\n    },\n    async deleteSession(sessionToken) {\n      await client.query(\n        `delete Session filter .sessionToken = <str>$sessionToken`,\n        { sessionToken }\n      )\n    },\n    async createVerificationToken({ identifier, expires, token }) {\n      const createdVerificationToken =\n        await client.querySingle<VerificationToken>(\n          `\n        select (\n          insert VerificationToken {\n            identifier := <str>$identifier,\n            expires := <datetime>$expires,\n            token := <str>$token,\n          }\n        ) {\n          identifier,\n          expires,\n          token\n        }\n        `,\n          { identifier, expires, token }\n        )\n\n      return createdVerificationToken\n    },\n    async useVerificationToken({ token, identifier }) {\n      const verificationToken = await client.querySingle<VerificationToken>(\n        `\n        select (\n          delete VerificationToken filter .token = <str>$token\n          and\n          .identifier = <str>$identifier\n        ) {\n          identifier,\n          expires,\n          token\n        }\n        `,\n        { token, identifier }\n      )\n\n      if (verificationToken && \"id\" in verificationToken) {\n        delete verificationToken.id\n      }\n      return verificationToken\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-edgedb/test/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { EdgeDBAdapter } from \"../src\"\nimport { createClient } from \"edgedb\"\n\nconst client = createClient()\n\nrunBasicTests({\n  adapter: EdgeDBAdapter(client),\n  db: {\n    connect: async () => {\n      await client.query(`\n        delete User;\n        delete Account;\n        delete Session;\n        delete VerificationToken;\n      `)\n    },\n    disconnect: async () => {\n      await client.query(`\n        delete User;\n        delete Account;\n        delete Session;\n        delete VerificationToken;\n      `)\n    },\n    user: async (id) => {\n      return await client.querySingle(\n        `\n      select User {\n        id,\n        email,\n        emailVerified,\n        name,\n        image\n      } filter .id = <uuid>$id\n      `,\n        { id }\n      )\n    },\n    account: async ({ providerAccountId, provider }) => {\n      return await client.querySingle(\n        `\n      select Account {\n        provider,\n        providerAccountId,\n        type,\n        access_token,\n        expires_at,\n        id_token,\n        refresh_token,\n        token_type,\n        scope,\n        session_state,\n        id,\n        userId\n      } \n      filter \n        .providerAccountId = <str>$providerAccountId\n      and\n        .provider = <str>$provider \n      `,\n        { providerAccountId, provider }\n      )\n    },\n    session: async (sessionToken) => {\n      return await client.querySingle(\n        `\n      select Session {\n        userId,\n        id,\n        expires,\n        sessionToken,\n      }\n      filter .sessionToken = <str>$sessionToken\n      `,\n        { sessionToken }\n      )\n    },\n    async verificationToken({ token, identifier }) {\n      return await client.querySingle(\n        `\n      select VerificationToken {\n        identifier,\n        expires,\n        token,\n      }\n      filter .token = <str>$token\n      and\n        .identifier = <str>$identifier\n      `,\n        { token, identifier }\n      )\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-edgedb/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-edgedb/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/edgedb-adapter\",\n  entryFileName: \"../edgedb-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-fauna/.fauna-project",
    "content": "schema_directory=fauna\n"
  },
  {
    "path": "packages/adapter-fauna/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://fauna.com\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/fauna.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Fauna Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/fauna-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/fauna-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/fauna-adapter?color=green&label=@auth/fauna-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/fauna-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/fauna-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/fauna).\n"
  },
  {
    "path": "packages/adapter-fauna/fauna/account.fsl",
    "content": "collection Account {\n    index byUserId {\n        terms [.userId]\n    }\n    index byProviderAndProviderAccountId {\n        terms [.provider, .providerAccountId]\n    }\n}\n\ncollection CustomAccount {\n    index byUserId {\n        terms [.userId]\n    }\n    index byProviderAndProviderAccountId {\n        terms [.provider, .providerAccountId]\n    }\n}\n\n"
  },
  {
    "path": "packages/adapter-fauna/fauna/session.fsl",
    "content": "collection Session {\n    unique [.sessionToken]\n    index bySessionToken {\n        terms [.sessionToken]\n    }\n    index byUserId {\n        terms [.userId]\n    }\n}\n\ncollection CustomSession {\n    unique [.sessionToken]\n    index bySessionToken {\n        terms [.sessionToken]\n    }\n    index byUserId {\n        terms [.userId]\n    }\n}\n\n"
  },
  {
    "path": "packages/adapter-fauna/fauna/user.fsl",
    "content": "collection User {\n    unique [.email]\n    index byEmail {\n        terms [.email]\n    }\n}\n\ncollection CustomUser {\n    unique [.email]\n    index byEmail {\n        terms [.email]\n    }\n}\n"
  },
  {
    "path": "packages/adapter-fauna/fauna/verificationToken.fsl",
    "content": "collection VerificationToken {\n    index byIdentifierAndToken {\n        terms [.identifier, .token]\n    }\n}\n\ncollection CustomVerificationToken {\n    index byIdentifierAndToken {\n        terms [.identifier, .token]\n    }\n}\n\n"
  },
  {
    "path": "packages/adapter-fauna/package.json",
    "content": "{\n  \"name\": \"@auth/fauna-adapter\",\n  \"version\": \"3.11.0\",\n  \"description\": \"Fauna Adapter for Auth.js\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Bhanu Teja P\",\n  \"contributors\": [\n    \"Nico Domino <yo@ndo.dev>\",\n    \"Balázs Orbán <info@balazsorban.com>\",\n    \"Aske Hippe Brun <mail@askehippebrun.com>\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"fauna\",\n    \"faunadb\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"fauna\": \"fauna\",\n    \"build\": \"tsc\",\n    \"dev\": \"tsc -w\",\n    \"test\": \"./test/test.sh\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"fauna\": \"^1.3.1\"\n  },\n  \"devDependencies\": {\n    \"fauna\": \"^1.3.1\",\n    \"fauna-shell\": \"1.2.1\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-fauna/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: \"16px\"}}>\n *  <p>Official <a href=\"https://docs.fauna.com/fauna/current/\">Fauna</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://fauna.com/features\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/fauna.svg\" width=\"64\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/fauna-adapter fauna\n * ```\n *\n * @module @auth/fauna-adapter\n */\nimport {\n  Client,\n  TimeStub,\n  fql,\n  NullDocument,\n  QueryValue,\n  QueryValueObject,\n} from \"fauna\"\n\nimport type {\n  Adapter,\n  AdapterUser,\n  AdapterSession,\n  VerificationToken,\n  AdapterAccount,\n} from \"@auth/core/adapters\"\n\ntype ToFauna<T> = {\n  [P in keyof T]: T[P] extends Date | null\n    ? TimeStub | null\n    : T[P] extends undefined\n      ? null\n      : T[P] extends QueryValue\n        ? T[P]\n        : QueryValueObject\n}\n\nexport type FaunaUser = ToFauna<AdapterUser>\nexport type FaunaSession = ToFauna<AdapterSession>\nexport type FaunaVerificationToken = ToFauna<VerificationToken> & { id: string }\nexport type FaunaAccount = ToFauna<AdapterAccount>\n\ntype AdapterConfig = {\n  collectionNames: {\n    user: string\n    session: string\n    account: string\n    verificationToken: string\n  }\n}\n\nconst defaultCollectionNames = {\n  user: \"User\",\n  session: \"Session\",\n  account: \"Account\",\n  verificationToken: \"VerificationToken\",\n}\n\nexport function FaunaAdapter(client: Client, config?: AdapterConfig): Adapter {\n  const { collectionNames = defaultCollectionNames } = config || {}\n\n  return {\n    async createUser(user) {\n      const response = await client.query<FaunaUser>(\n        fql`Collection(${collectionNames.user}).create(${format.to(user)})`\n      )\n      return format.from(response.data)\n    },\n    async getUser(id) {\n      const response = await client.query<FaunaUser | NullDocument>(\n        fql`Collection(${collectionNames.user}).byId(${id})`\n      )\n      if (response.data instanceof NullDocument) return null\n      return format.from(response.data)\n    },\n    async getUserByEmail(email) {\n      const response = await client.query<FaunaUser>(\n        fql`Collection(${collectionNames.user}).byEmail(${email}).first()`\n      )\n      if (response.data === null) return null\n      return format.from(response.data)\n    },\n    async getUserByAccount({ provider, providerAccountId }) {\n      const response = await client.query<FaunaUser>(fql`\n        let account = Collection(${collectionNames.account}).byProviderAndProviderAccountId(${provider}, ${providerAccountId}).first()\n        if (account != null) {\n          Collection(${collectionNames.user}).byId(account.userId)\n        } else {\n          null\n        }\n      `)\n      return format.from(response.data)\n    },\n    async updateUser(user) {\n      const _user: Partial<AdapterUser> = { ...user }\n      delete _user.id\n      const response = await client.query<FaunaUser>(\n        fql`Collection(${collectionNames.user}).byId(${\n          user.id\n        }).update(${format.to(_user)})`\n      )\n      return format.from(response.data)\n    },\n    async deleteUser(userId) {\n      await client.query(fql`\n        // Delete the user's sessions\n        Collection(${collectionNames.session}).byUserId(${userId}).forEach(session => session.delete())\n        \n        // Delete the user's accounts\n        Collection(${collectionNames.account}).byUserId(${userId}).forEach(account => account.delete())\n        \n        // Delete the user\n        Collection(${collectionNames.user}).byId(${userId}).delete()\n      `)\n    },\n    async linkAccount(account) {\n      await client.query<FaunaAccount>(\n        fql`Collection(${collectionNames.account}).create(${format.to(\n          account\n        )})`\n      )\n      return account\n    },\n    async unlinkAccount({ provider, providerAccountId }) {\n      const response = await client.query<FaunaAccount>(\n        fql`Collection(${collectionNames.account}).byProviderAndProviderAccountId(${provider}, ${providerAccountId}).first().delete()`\n      )\n      return format.from<AdapterAccount>(response.data)\n    },\n    async getSessionAndUser(sessionToken) {\n      const response = await client.query<[FaunaUser, FaunaSession]>(fql`\n        let session = Collection(${collectionNames.session}).bySessionToken(${sessionToken}).first()\n        if (session != null) {\n          let user = Collection(${collectionNames.user}).byId(session.userId)\n          if (user != null) {\n            [user, session]\n          } else {\n            null\n          }\n        } else {\n          null\n        }\n      `)\n      if (response.data === null) return null\n      const [user, session] = response.data ?? []\n      return { session: format.from(session), user: format.from(user) }\n    },\n    async createSession(session) {\n      await client.query<FaunaSession>(\n        fql`Collection(${collectionNames.session}).create(${format.to(\n          session\n        )})`\n      )\n      return session\n    },\n    async updateSession(session) {\n      const response = await client.query<FaunaSession>(\n        fql`Collection(${collectionNames.session}).bySessionToken(${\n          session.sessionToken\n        }).first().update(${format.to(session)})`\n      )\n      return format.from(response.data)\n    },\n    async deleteSession(sessionToken) {\n      await client.query(\n        fql`Collection(${collectionNames.session}).bySessionToken(${sessionToken}).first().delete()`\n      )\n    },\n    async createVerificationToken(verificationToken) {\n      await client.query<FaunaVerificationToken>(\n        fql`Collection(${collectionNames.verificationToken}).create(${format.to(\n          verificationToken\n        )})`\n      )\n      return verificationToken\n    },\n    async useVerificationToken({ identifier, token }) {\n      const response = await client.query<FaunaVerificationToken>(\n        fql`Collection(${collectionNames.verificationToken}).byIdentifierAndToken(${identifier}, ${token}).first()`\n      )\n      if (response.data === null) return null\n      // Delete the verification token so it can only be used once\n      await client.query(\n        fql`Collection(${collectionNames.verificationToken}).byId(${response.data.id}).delete()`\n      )\n      const _verificationToken: Partial<FaunaVerificationToken> = {\n        ...response.data,\n      }\n      delete _verificationToken.id\n      return format.from(_verificationToken)\n    },\n  }\n}\n\nexport const format = {\n  /** Takes an object that's coming from the database and converts it to plain JavaScript. */\n  from<T>(object: Record<string, any> = {}): T {\n    if (!object) return null as unknown as T\n    const newObject: Record<string, unknown> = {}\n    for (const [key, value] of Object.entries(object))\n      if (key === \"coll\" || key === \"ts\") continue\n      else if (value instanceof TimeStub) newObject[key] = value.toDate()\n      else newObject[key] = value\n    return newObject as T\n  },\n  /** Takes an object that's coming from Auth.js and prepares it to be written to the database. */\n  to<T>(object: Record<string, any>): T {\n    const newObject: Record<string, unknown> = {}\n    for (const [key, value] of Object.entries(object))\n      if (value instanceof Date) newObject[key] = TimeStub.fromDate(value)\n      else if (typeof value === \"string\" && !isNaN(Date.parse(value)))\n        newObject[key] = TimeStub.from(value)\n      else newObject[key] = value ?? null\n\n    return newObject as T\n  },\n}\n"
  },
  {
    "path": "packages/adapter-fauna/test/index.test.ts",
    "content": "import { FaunaAdapter, format } from \"../src\"\nimport { runBasicTests } from \"utils/adapter\"\nimport { Client, fql, NullDocument } from \"fauna\"\n\nimport type {\n  FaunaUser,\n  FaunaAccount,\n  FaunaSession,\n  FaunaVerificationToken,\n} from \"../src\"\n\nconst client = new Client({\n  secret: \"secret\",\n  endpoint: new URL(\"http://localhost:8443\"),\n})\n\nrunBasicTests({\n  adapter: FaunaAdapter(client),\n  db: {\n    // UUID is not a valid ID in Fauna (see https://docs.fauna.com/fauna/current/reference/fql_reference/types#id)\n    id: () => String(Math.floor(Math.random() * 10 ** 18)),\n    user: async (id) => {\n      const response = await client.query<FaunaUser>(fql`User.byId(${id})`)\n      if (response.data instanceof NullDocument) return null\n      return format.from(response.data)\n    },\n    async session(sessionToken) {\n      const response = await client.query<FaunaSession>(\n        fql`Session.bySessionToken(${sessionToken}).first()`\n      )\n      return format.from(response.data)\n    },\n    async account({ provider, providerAccountId }) {\n      const response = await client.query<FaunaAccount>(\n        fql`Account.byProviderAndProviderAccountId(${provider}, ${providerAccountId}).first()`\n      )\n      return format.from(response.data)\n    },\n    async verificationToken({ identifier, token }) {\n      const response = await client.query<FaunaVerificationToken>(\n        fql`VerificationToken.byIdentifierAndToken(${identifier}, ${token}).first()`\n      )\n      const _verificationToken: Partial<FaunaVerificationToken> = {\n        ...response.data,\n      }\n      delete _verificationToken.id\n      return format.from(_verificationToken)\n    },\n  },\n})\n\nrunBasicTests({\n  adapter: FaunaAdapter(client, {\n    collectionNames: {\n      user: \"CustomUser\",\n      account: \"CustomAccount\",\n      session: \"CustomSession\",\n      verificationToken: \"CustomVerificationToken\",\n    },\n  }),\n  db: {\n    // UUID is not a valid ID in Fauna (see https://docs.fauna.com/fauna/current/reference/fql_reference/types#id)\n    id: () => String(Math.floor(Math.random() * 10 ** 18)),\n    user: async (id) => {\n      const response = await client.query<FaunaUser>(\n        fql`CustomUser.byId(${id})`\n      )\n      if (response.data instanceof NullDocument) return null\n      return format.from(response.data)\n    },\n    async session(sessionToken) {\n      const response = await client.query<FaunaSession>(\n        fql`CustomSession.bySessionToken(${sessionToken}).first()`\n      )\n      return format.from(response.data)\n    },\n    async account({ provider, providerAccountId }) {\n      const response = await client.query<FaunaAccount>(\n        fql`CustomAccount.byProviderAndProviderAccountId(${provider}, ${providerAccountId}).first()`\n      )\n      return format.from(response.data)\n    },\n    async verificationToken({ identifier, token }) {\n      const response = await client.query<FaunaVerificationToken>(\n        fql`CustomVerificationToken.byIdentifierAndToken(${identifier}, ${token}).first()`\n      )\n      const _verificationToken: Partial<FaunaVerificationToken> = {\n        ...response.data,\n      }\n      delete _verificationToken.id\n      return format.from(_verificationToken)\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-fauna/test/test.sh",
    "content": "#!/usr/bin/env bash\n\nCONTAINER_NAME=authjs-fauna-test\nFAUNADB_PORT=8443\nFAUNA_ADMIN_KEY=secret\n\n# Start Docker\ndocker run -d --rm \\\n  --name ${CONTAINER_NAME} \\\n  -p ${FAUNADB_PORT}:${FAUNADB_PORT} \\\n  fauna/faunadb\n\necho \"Waiting 15s for db to start...\"\nsleep 15\n\n# Create collections and indexes\nfauna schema push --url=http://localhost:8443 --force --secret=${FAUNA_ADMIN_KEY}\n\n# Always stop container, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  docker stop ${CONTAINER_NAME}\nelse\n  docker stop ${CONTAINER_NAME} && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-fauna/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-fauna/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/fauna-adapter\",\n  entryFileName: \"../fauna-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-firebase/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://firebase.google.com\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/firebase.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Firebase Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/firebase-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/firebase-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/firebase-adapter?color=green&label=@auth/firebase-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/firebase-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/firebase-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/firebase).\n"
  },
  {
    "path": "packages/adapter-firebase/package.json",
    "content": "{\n  \"name\": \"@auth/firebase-adapter\",\n  \"version\": \"2.11.0\",\n  \"description\": \"Firebase adapter for Auth.js\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Ron Houben <ron.houben85@gmail.com>\",\n  \"contributors\": [\n    \"Nico Domino <yo@ndo.dev>\",\n    \"Alex Meuer <github@alexmeuer.com>\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"firebase\",\n    \"firebase-admin\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"tsc\",\n    \"test\": \"./test/test.sh\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"firebase-admin\": \"^12.0.0 || ^13.0.0\"\n  },\n  \"devDependencies\": {\n    \"firebase-admin\": \"^13.5.0\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-firebase/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>Official **Firestore** adapter for Auth.js / NextAuth.js, using the [Firebase Admin SDK](https://firebase.google.com/docs/admin/setup)</p>\n *  <a href=\"https://firebase.google.com/docs/firestore/\">\n *   <img style={{display: \"block\"}} src=\"/img/adapters/firebase.svg\" width=\"48\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/firebase-adapter firebase-admin\n * ```\n *\n * @module @auth/firebase-adapter\n */\n\nimport { type AppOptions, getApps, initializeApp } from \"firebase-admin/app\"\n\nimport {\n  Firestore,\n  getFirestore,\n  initializeFirestore,\n  Timestamp,\n} from \"firebase-admin/firestore\"\n\nimport type {\n  Adapter,\n  AdapterUser,\n  AdapterAccount,\n  AdapterSession,\n  VerificationToken,\n} from \"@auth/core/adapters\"\n\n/** Configure the Firebase Adapter. */\nexport interface FirebaseAdapterConfig extends AppOptions {\n  /**\n   * The name of the app passed to {@link https://firebase.google.com/docs/reference/admin/node/firebase-admin.md#initializeapp `initializeApp()`}.\n   */\n  name?: string\n  firestore?: Firestore\n  /**\n   * Use this option if mixed `snake_case` and `camelCase` field names in the database is an issue for you.\n   * Passing `snake_case` will convert all field and collection names to `snake_case`.\n   * E.g. the collection `verificationTokens` will be `verification_tokens`,\n   * and fields like `emailVerified` will be `email_verified` instead.\n   *\n   *\n   * @example\n   * ```ts\n   *  // This will convert all field and collection names to snake_case\n   *  adapter: FirestoreAdapter({ namingStrategy: \"snake_case\" })\n   *  // ...\n   * })\n   * ```\n   */\n  namingStrategy?: \"snake_case\" | \"default\"\n  /**\n   * Use this option if you already have one of the default collections in your Firestore database.\n   *\n   * @example\n   * ```ts\n   *  // This will use the collection name \"authjs_users\" instead of the default \"users\"\n   *  adapter: FirestoreAdapter({ collections: { users: \"authjs_users\" } })\n   *  // ...\n   * ```\n   */\n  collections?: {\n    users?: string\n    sessions?: string\n    accounts?: string\n    verificationTokens?: string\n  }\n}\n\nexport function FirestoreAdapter(\n  config?: FirebaseAdapterConfig | Firestore\n): Adapter {\n  const {\n    db,\n    namingStrategy = \"default\",\n    collections = {},\n  } = config instanceof Firestore\n    ? { db: config }\n    : { ...config, db: config?.firestore ?? initFirestore(config) }\n\n  const preferSnakeCase = namingStrategy === \"snake_case\"\n  const C = collectionsFactory(db, preferSnakeCase, {\n    users: \"users\",\n    sessions: \"sessions\",\n    accounts: \"accounts\",\n    verificationTokens: preferSnakeCase\n      ? \"verification_tokens\"\n      : \"verificationTokens\",\n    ...collections,\n  })\n  const mapper = mapFieldsFactory(preferSnakeCase)\n\n  return {\n    async createUser(userInit) {\n      const { id: userId } = await C.users.add(userInit as AdapterUser)\n\n      const user = await getDoc(C.users.doc(userId))\n      if (!user) throw new Error(\"[createUser] Failed to fetch created user\")\n\n      return user\n    },\n\n    async getUser(id) {\n      return await getDoc(C.users.doc(id))\n    },\n\n    async getUserByEmail(email) {\n      return await getOneDoc(C.users.where(\"email\", \"==\", email))\n    },\n\n    async getUserByAccount({ provider, providerAccountId }) {\n      const account = await getOneDoc(\n        C.accounts\n          .where(\"provider\", \"==\", provider)\n          .where(mapper.toDb(\"providerAccountId\"), \"==\", providerAccountId)\n      )\n      if (!account) return null\n\n      return await getDoc(C.users.doc(account.userId))\n    },\n\n    async updateUser(partialUser) {\n      if (!partialUser.id) throw new Error(\"[updateUser] Missing id\")\n\n      const userRef = C.users.doc(partialUser.id)\n\n      await userRef.set(partialUser, { merge: true })\n\n      const user = await getDoc(userRef)\n      if (!user) throw new Error(\"[updateUser] Failed to fetch updated user\")\n\n      return user\n    },\n\n    async deleteUser(userId) {\n      await db.runTransaction(async (transaction) => {\n        const accounts = await C.accounts\n          .where(mapper.toDb(\"userId\"), \"==\", userId)\n          .get()\n        const sessions = await C.sessions\n          .where(mapper.toDb(\"userId\"), \"==\", userId)\n          .get()\n\n        transaction.delete(C.users.doc(userId))\n\n        accounts.forEach((account) => transaction.delete(account.ref))\n        sessions.forEach((session) => transaction.delete(session.ref))\n      })\n    },\n\n    async linkAccount(accountInit) {\n      const ref = await C.accounts.add(accountInit)\n      const account = await ref.get().then((doc) => doc.data())\n      return account ?? null\n    },\n\n    async unlinkAccount({ provider, providerAccountId }) {\n      await deleteDocs(\n        C.accounts\n          .where(\"provider\", \"==\", provider)\n          .where(mapper.toDb(\"providerAccountId\"), \"==\", providerAccountId)\n          .limit(1)\n      )\n    },\n\n    async createSession(sessionInit) {\n      const ref = await C.sessions.add(sessionInit)\n      const session = await ref.get().then((doc) => doc.data())\n\n      if (session) return session ?? null\n\n      throw new Error(\"[createSession] Failed to fetch created session\")\n    },\n\n    async getSessionAndUser(sessionToken) {\n      const session = await getOneDoc(\n        C.sessions.where(mapper.toDb(\"sessionToken\"), \"==\", sessionToken)\n      )\n      if (!session) return null\n\n      const user = await getDoc(C.users.doc(session.userId))\n      if (!user) return null\n\n      return { session, user }\n    },\n\n    async updateSession(partialSession) {\n      const sessionId = await db.runTransaction(async (transaction) => {\n        const sessionSnapshot = (\n          await transaction.get(\n            C.sessions\n              .where(\n                mapper.toDb(\"sessionToken\"),\n                \"==\",\n                partialSession.sessionToken\n              )\n              .limit(1)\n          )\n        ).docs[0]\n        if (!sessionSnapshot?.exists) return null\n\n        transaction.set(sessionSnapshot.ref, partialSession, { merge: true })\n\n        return sessionSnapshot.id\n      })\n\n      if (!sessionId) return null\n\n      const session = await getDoc(C.sessions.doc(sessionId))\n      if (session) return session\n      throw new Error(\"[updateSession] Failed to fetch updated session\")\n    },\n\n    async deleteSession(sessionToken) {\n      await deleteDocs(\n        C.sessions\n          .where(mapper.toDb(\"sessionToken\"), \"==\", sessionToken)\n          .limit(1)\n      )\n    },\n\n    async createVerificationToken(verificationToken) {\n      await C.verification_tokens.add(verificationToken)\n      return verificationToken\n    },\n\n    async useVerificationToken({ identifier, token }) {\n      const verificationTokenSnapshot = (\n        await C.verification_tokens\n          .where(\"identifier\", \"==\", identifier)\n          .where(\"token\", \"==\", token)\n          .limit(1)\n          .get()\n      ).docs[0]\n\n      if (!verificationTokenSnapshot) return null\n\n      const data = verificationTokenSnapshot.data()\n      await verificationTokenSnapshot.ref.delete()\n      return data\n    },\n  }\n}\n\n// for consistency, store all fields as snake_case in the database\nconst MAP_TO_FIRESTORE: Record<string, string | undefined> = {\n  userId: \"user_id\",\n  sessionToken: \"session_token\",\n  providerAccountId: \"provider_account_id\",\n  emailVerified: \"email_verified\",\n}\nconst MAP_FROM_FIRESTORE: Record<string, string | undefined> = {}\n\nfor (const key in MAP_TO_FIRESTORE) {\n  MAP_FROM_FIRESTORE[MAP_TO_FIRESTORE[key]!] = key\n}\n\nconst identity = <T>(x: T) => x\n\n/** @internal */\nexport function mapFieldsFactory(preferSnakeCase?: boolean) {\n  if (preferSnakeCase) {\n    return {\n      toDb: (field: string) => MAP_TO_FIRESTORE[field] ?? field,\n      fromDb: (field: string) => MAP_FROM_FIRESTORE[field] ?? field,\n    }\n  }\n  return { toDb: identity, fromDb: identity }\n}\n\nfunction getConverter<Document extends Record<string, any>>(options: {\n  excludeId?: boolean\n  preferSnakeCase?: boolean\n}): FirebaseFirestore.FirestoreDataConverter<Document> {\n  const mapper = mapFieldsFactory(options?.preferSnakeCase)\n\n  return {\n    toFirestore(object) {\n      const document: Record<string, unknown> = {}\n\n      for (const key in object) {\n        if (key === \"id\") continue\n        const value = object[key]\n        if (value !== undefined) {\n          document[mapper.toDb(key)] = value\n        } else {\n          console.warn(`FirebaseAdapter: value for key \"${key}\" is undefined`)\n        }\n      }\n\n      return document\n    },\n\n    fromFirestore(\n      snapshot: FirebaseFirestore.QueryDocumentSnapshot<Document>\n    ): Document {\n      const document = snapshot.data()! // we can guarantee it exists\n\n      const object: Record<string, unknown> = {}\n\n      if (!options?.excludeId) {\n        object.id = snapshot.id\n      }\n\n      for (const key in document) {\n        let value: any = document[key]\n        if (value instanceof Timestamp) value = value.toDate()\n\n        object[mapper.fromDb(key)] = value\n      }\n\n      return object as Document\n    },\n  }\n}\n\n/** @internal */\nexport async function getOneDoc<T>(\n  querySnapshot: FirebaseFirestore.Query<T>\n): Promise<T | null> {\n  const querySnap = await querySnapshot.limit(1).get()\n  return querySnap.docs[0]?.data() ?? null\n}\n\nasync function deleteDocs<T>(\n  querySnapshot: FirebaseFirestore.Query<T>\n): Promise<void> {\n  const querySnap = await querySnapshot.get()\n  for (const doc of querySnap.docs) {\n    await doc.ref.delete()\n  }\n}\n\n/** @internal */\nexport async function getDoc<T>(\n  docRef: FirebaseFirestore.DocumentReference<T>\n): Promise<T | null> {\n  const docSnap = await docRef.get()\n  return docSnap.data() ?? null\n}\n\n/** @internal */\nexport function collectionsFactory(\n  db: FirebaseFirestore.Firestore,\n  preferSnakeCase = false,\n  collections: Required<NonNullable<FirebaseAdapterConfig[\"collections\"]>>\n) {\n  return {\n    users: db\n      .collection(collections.users)\n      .withConverter(getConverter<AdapterUser>({ preferSnakeCase })),\n    sessions: db\n      .collection(collections.sessions)\n      .withConverter(getConverter<AdapterSession>({ preferSnakeCase })),\n    accounts: db\n      .collection(collections.accounts)\n      .withConverter(getConverter<AdapterAccount>({ preferSnakeCase })),\n    verification_tokens: db\n      .collection(collections.verificationTokens)\n      .withConverter(\n        getConverter<VerificationToken>({ preferSnakeCase, excludeId: true })\n      ),\n  }\n}\n\n/**\n * Utility function that helps making sure that there is no duplicate app initialization issues in serverless environments.\n * If no parameter is passed, it will use the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to initialize a Firestore instance.\n *\n * @example\n * ```ts title=\"lib/firestore.ts\"\n * import { initFirestore } from \"@auth/firebase-adapter\"\n * import { cert } from \"firebase-admin/app\"\n *\n * export const firestore = initFirestore({\n *  credential: cert({\n *    projectId: process.env.FIREBASE_PROJECT_ID,\n *    clientEmail: process.env.FIREBASE_CLIENT_EMAIL,\n *    privateKey: process.env.FIREBASE_PRIVATE_KEY,\n *  })\n * })\n * ```\n */\nexport function initFirestore(\n  options: AppOptions & { name?: FirebaseAdapterConfig[\"name\"] } = {}\n) {\n  const apps = getApps()\n  const app = options.name ? apps.find((a) => a.name === options.name) : apps[0]\n\n  if (app) return getFirestore(app)\n\n  return initializeFirestore(initializeApp(options, options.name))\n}\n"
  },
  {
    "path": "packages/adapter-firebase/test/firestore.rules",
    "content": "rules_version = '2';\n\n// Deny read/write access to all users under any conditions\nservice cloud.firestore {\n  match /databases/{database}/documents {\n    match /{document=**} {\n      allow read, write: if false;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/adapter-firebase/test/index.test.ts",
    "content": "import { describe, expect, test } from \"vitest\"\nimport { runBasicTests } from \"utils/adapter\"\n\nimport { FirestoreAdapter, type FirebaseAdapterConfig } from \"../src\"\nimport {\n  collectionsFactory,\n  initFirestore,\n  getDoc,\n  getOneDoc,\n  mapFieldsFactory,\n} from \"../src\"\n\n// TODO: promises never resolve in tests, so currently we are skipping them\ndescribe.each([\n  { namingStrategy: \"snake_case\" },\n  { namingStrategy: \"default\" },\n] as Partial<FirebaseAdapterConfig>[])(\n  \"FirebaseAdapter with config: %s\",\n  (config) => {\n    config.name = `authjs-test-${config.namingStrategy}`\n    config.projectId = \"authjs-test\"\n\n    const preferSnakeCase = config.namingStrategy === \"snake_case\"\n\n    config.collections = {\n      accounts: preferSnakeCase ? \"authjs_accounts\" : \"authjsAccounts\",\n      verificationTokens: preferSnakeCase\n        ? \"verification_tokens\"\n        : \"verificationTokens\",\n    }\n    const db = initFirestore(config)\n    const mapper = mapFieldsFactory(preferSnakeCase)\n    const C = collectionsFactory(db, preferSnakeCase, {\n      users: \"users\",\n      accounts: preferSnakeCase ? \"authjs_accounts\" : \"authjsAccounts\",\n      sessions: \"sessions\",\n      verificationTokens: preferSnakeCase\n        ? \"verification_tokens\"\n        : \"verificationTokens\",\n    })\n\n    for (const [name, collection] of Object.entries(C)) {\n      test(`collection \"${name}\" should be empty`, async () => {\n        expect((await collection.count().get()).data().count).toBe(0)\n      })\n    }\n\n    runBasicTests({\n      adapter: FirestoreAdapter(config),\n      db: {\n        disconnect: async () => await db.terminate(),\n        session: (sessionToken) =>\n          getOneDoc(\n            C.sessions.where(mapper.toDb(\"sessionToken\"), \"==\", sessionToken)\n          ),\n        user: (userId) => getDoc(C.users.doc(userId)),\n        account: ({ provider, providerAccountId }) =>\n          getOneDoc(\n            C.accounts\n              .where(\"provider\", \"==\", provider)\n              .where(mapper.toDb(\"providerAccountId\"), \"==\", providerAccountId)\n          ),\n        verificationToken: ({ identifier, token }) =>\n          getOneDoc(\n            C.verification_tokens\n              .where(\"identifier\", \"==\", identifier)\n              .where(\"token\", \"==\", token)\n          ),\n      },\n    })\n  }\n)\n"
  },
  {
    "path": "packages/adapter-firebase/test/test.sh",
    "content": "#!/usr/bin/env bash\n\nCONTAINER_NAME=authjs-firestore-test\n\n# Start Docker\ndocker run -d --rm \\\n  --name ${CONTAINER_NAME} \\\n  -p 8080:8080 \\\n  -v \"$(pwd)/test/firestore.rules\":/firestore.rules \\\n  google/cloud-sdk:emulators gcloud beta emulators firestore start \\\n  --host-port=0.0.0.0:8080 \\\n  --rules=/firestore.rules\n\necho \"Waiting 10s for db to start...\"\nsleep 10\n\nexport FIRESTORE_EMULATOR_HOST=localhost:8080\n# Always stop container, but exit with 1 when tests are failing\nif pnpm exec vitest run -c vitest.config.ts; then\n  docker stop ${CONTAINER_NAME}\nelse\n  docker stop ${CONTAINER_NAME} && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-firebase/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-firebase/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/firebase-adapter\",\n  entryFileName: \"../firebase-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-firebase/vitest.config.ts",
    "content": "/// <reference types=\"vitest\" />\n\nimport { defineConfig, mergeConfig } from \"vite\"\nimport baseConfig from \"../utils/vitest.config\"\n\n// https://vitejs.dev/config/\nexport default mergeConfig(\n  baseConfig,\n  defineConfig({\n    test: {\n      testTimeout: 30000,\n      hookTimeout: 30000,\n    },\n  })\n)\n"
  },
  {
    "path": "packages/adapter-hasura/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://hasura.io\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/hasura.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Hasura Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/hasura-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/hasura-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/hasura-adapter?color=green&label=@auth/hasura-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/hasura-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/hasura-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/hasura).\n\n## Credit\n\nBased on code from [Amruth Pillai](https://github.com/AmruthPillai)\n"
  },
  {
    "path": "packages/adapter-hasura/codegen.ts",
    "content": "import type { CodegenConfig } from \"@graphql-codegen/cli\"\n\nexport default {\n  overwrite: true,\n  schema: \"schema.gql\",\n  emitLegacyCommonJSImports: false,\n  documents: \"src/queries/*.graphql\",\n  generates: {\n    \"src/lib/generated/\": {\n      preset: \"client\",\n      config: {\n        documentMode: \"string\",\n        skipTypename: true,\n        enumsAsTypes: true,\n        strictScalars: true,\n        useTypeImports: true,\n        scalars: {\n          timestamptz: \"string\",\n          uuid: \"string\",\n        },\n      },\n    },\n  },\n} satisfies CodegenConfig\n"
  },
  {
    "path": "packages/adapter-hasura/docker-compose.yml",
    "content": "services:\n  postgres:\n    image: postgres:16\n    volumes:\n      - db_data:/var/lib/postgresql/data\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n      interval: 5s\n      timeout: 5s\n      retries: 5\n    environment:\n      POSTGRES_PASSWORD: postgrespassword\n  graphql-engine:\n    image: hasura/graphql-engine:v2.33.4.cli-migrations-v3\n    ports:\n      - \"8080:8080\"\n    depends_on:\n      postgres:\n        condition: service_healthy\n    environment:\n      ## postgres database to store Hasura metadata\n      HASURA_GRAPHQL_METADATA_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres\n      ## this env var can be used to add the above postgres database to Hasura as a data source. this can be removed/updated based on your needs\n      PG_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres\n      ## enable the console served by server\n      HASURA_GRAPHQL_ENABLE_CONSOLE: \"true\" # set to \"false\" to disable console\n      ## enable debugging mode. It is recommended to disable this in production\n      HASURA_GRAPHQL_DEV_MODE: \"true\"\n      HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log\n      ## uncomment next line to run console offline (i.e load console assets from server instead of CDN)\n      # HASURA_GRAPHQL_CONSOLE_ASSETS_DIR: /srv/console-assets\n      ## uncomment next line to set an admin secret\n      HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey\n    volumes:\n      - ./hasura/migrations:/hasura-migrations\n      - ./hasura/metadata:/hasura-metadata\nvolumes:\n  db_data:\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/config.yaml",
    "content": "version: 3\nendpoint: http://localhost:8080\nmetadata_directory: metadata\nactions:\n  kind: synchronous\n  handler_webhook_baseurl: http://localhost:3000\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/actions.graphql",
    "content": ""
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/actions.yaml",
    "content": "actions: []\ncustom_types:\n  enums: []\n  input_objects: []\n  objects: []\n  scalars: []\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/allow_list.yaml",
    "content": "[]\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/api_limits.yaml",
    "content": "{}\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/cron_triggers.yaml",
    "content": "[]\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/databases/databases.yaml",
    "content": "- name: default\n  kind: postgres\n  configuration:\n    connection_info:\n      database_url:\n        from_env: PG_DATABASE_URL\n      isolation_level: read-committed\n      use_prepared_statements: false\n  customization:\n    naming_convention: hasura-default\n  tables: \"!include default/tables/tables.yaml\"\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/databases/default/tables/public_accounts.yaml",
    "content": "table:\n  name: accounts\n  schema: public\nobject_relationships:\n  - name: user\n    using:\n      foreign_key_constraint_on: userId\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/databases/default/tables/public_provider_type.yaml",
    "content": "table:\n  name: provider_type\n  schema: public\nis_enum: true\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/databases/default/tables/public_sessions.yaml",
    "content": "table:\n  name: sessions\n  schema: public\nobject_relationships:\n  - name: user\n    using:\n      foreign_key_constraint_on: userId\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/databases/default/tables/public_users.yaml",
    "content": "table:\n  name: users\n  schema: public\narray_relationships:\n  - name: accounts\n    using:\n      foreign_key_constraint_on:\n        column: userId\n        table:\n          name: accounts\n          schema: public\n  - name: sessions\n    using:\n      foreign_key_constraint_on:\n        column: userId\n        table:\n          name: sessions\n          schema: public\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/databases/default/tables/public_verification_tokens.yaml",
    "content": "table:\n  name: verification_tokens\n  schema: public\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/databases/default/tables/tables.yaml",
    "content": "- \"!include public_accounts.yaml\"\n- \"!include public_provider_type.yaml\"\n- \"!include public_sessions.yaml\"\n- \"!include public_users.yaml\"\n- \"!include public_verification_tokens.yaml\"\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/graphql_schema_introspection.yaml",
    "content": "disabled_for_roles: []\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/inherited_roles.yaml",
    "content": "[]\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/network.yaml",
    "content": "{}\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/query_collections.yaml",
    "content": "[]\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/remote_schemas.yaml",
    "content": "[]\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/rest_endpoints.yaml",
    "content": "[]\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/metadata/version.yaml",
    "content": "version: 3\n"
  },
  {
    "path": "packages/adapter-hasura/hasura/migrations/default/1666885939998_init_nextauth_models/up.sql",
    "content": "CREATE TABLE accounts (\n    id uuid DEFAULT gen_random_uuid() NOT NULL,\n    type text NOT NULL,\n    provider text NOT NULL,\n    \"providerAccountId\" text NOT NULL,\n    refresh_token text,\n    access_token text,\n    expires_at integer,\n    token_type text,\n    scope text,\n    id_token text,\n    session_state text,\n    \"userId\" uuid NOT NULL\n);\n\nCREATE TABLE sessions (\n    id uuid DEFAULT gen_random_uuid() NOT NULL,\n    \"sessionToken\" text NOT NULL,\n    \"userId\" uuid NOT NULL,\n    expires timestamptz NOT NULL\n);\n\nCREATE TABLE users (\n    id uuid DEFAULT gen_random_uuid() NOT NULL,\n    name text,\n    email text NOT NULL,\n    \"emailVerified\" timestamptz,\n    image text\n);\n\nCREATE TABLE verification_tokens (\n    token text NOT NULL,\n    identifier text NOT NULL,\n    expires timestamptz NOT NULL\n);\n\nCREATE TABLE provider_type (\n    value text NOT NULL\n);\n\nALTER TABLE ONLY accounts\n    ADD CONSTRAINT accounts_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY sessions\n    ADD CONSTRAINT sessions_pkey PRIMARY KEY (\"sessionToken\");\n\nALTER TABLE ONLY users\n    ADD CONSTRAINT users_email_key UNIQUE (email);\n\nALTER TABLE ONLY users\n    ADD CONSTRAINT users_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY verification_tokens\n    ADD CONSTRAINT verification_tokens_pkey PRIMARY KEY (token);\n\nALTER TABLE ONLY provider_type\n    ADD CONSTRAINT provider_type_pkey PRIMARY KEY (value);\n\nALTER TABLE ONLY accounts\n    ADD CONSTRAINT \"accounts_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES public.users(id) ON UPDATE RESTRICT ON DELETE CASCADE;\n\nALTER TABLE ONLY sessions\n    ADD CONSTRAINT \"sessions_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES public.users(id) ON UPDATE RESTRICT ON DELETE CASCADE;\n\nINSERT INTO provider_type (value) VALUES ('credentials'), ('email'), ('oauth'), ('oidc');\n\nALTER TABLE ONLY accounts\n    ADD CONSTRAINT \"accounts_type_fkey\" FOREIGN KEY (\"type\") REFERENCES public.provider_type(value) ON UPDATE RESTRICT ON DELETE RESTRICT;\n"
  },
  {
    "path": "packages/adapter-hasura/package.json",
    "content": "{\n  \"name\": \"@auth/hasura-adapter\",\n  \"version\": \"1.11.0\",\n  \"description\": \"Hasura adapter for Auth.js.\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"contributors\": [\n    \"Hasura Team\",\n    \"Amruth Pillai <im.amruth@gmail.com>\",\n    \"Arjun Yelamanchili\"\n  ],\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"@auth\",\n    \"authjs\",\n    \"Auth.js\",\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"hasura\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"lib\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"test\": \"./test/test.sh\",\n    \"build\": \"graphql-codegen-esm --config codegen.ts && tsc\",\n    \"clean\": \"rm -rf *.js *.d.ts* lib\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"@graphql-codegen/cli\": \"^5.0.0\",\n    \"@graphql-codegen/client-preset\": \"^4.1.0\",\n    \"@graphql-typed-document-node/core\": \"^3.2.0\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-hasura/schema.gql",
    "content": "schema {\n  query: query_root\n  mutation: mutation_root\n  subscription: subscription_root\n}\n\n\"\"\"\nwhether this query should be cached (Hasura Cloud only)\n\"\"\"\ndirective @cached(\n  \"\"\"\n  measured in seconds\n  \"\"\"\n  ttl: Int! = 60\n\n  \"\"\"\n  refresh the cache entry\n  \"\"\"\n  refresh: Boolean! = false\n) on QUERY\n\n\"\"\"\nBoolean expression to compare columns of type \"Int\". All fields are combined with logical 'AND'.\n\"\"\"\ninput Int_comparison_exp {\n  _eq: Int\n  _gt: Int\n  _gte: Int\n  _in: [Int!]\n  _is_null: Boolean\n  _lt: Int\n  _lte: Int\n  _neq: Int\n  _nin: [Int!]\n}\n\n\"\"\"\nBoolean expression to compare columns of type \"String\". All fields are combined with logical 'AND'.\n\"\"\"\ninput String_comparison_exp {\n  _eq: String\n  _gt: String\n  _gte: String\n\n  \"\"\"\n  does the column match the given case-insensitive pattern\n  \"\"\"\n  _ilike: String\n  _in: [String!]\n\n  \"\"\"\n  does the column match the given POSIX regular expression, case insensitive\n  \"\"\"\n  _iregex: String\n  _is_null: Boolean\n\n  \"\"\"\n  does the column match the given pattern\n  \"\"\"\n  _like: String\n  _lt: String\n  _lte: String\n  _neq: String\n\n  \"\"\"\n  does the column NOT match the given case-insensitive pattern\n  \"\"\"\n  _nilike: String\n  _nin: [String!]\n\n  \"\"\"\n  does the column NOT match the given POSIX regular expression, case insensitive\n  \"\"\"\n  _niregex: String\n\n  \"\"\"\n  does the column NOT match the given pattern\n  \"\"\"\n  _nlike: String\n\n  \"\"\"\n  does the column NOT match the given POSIX regular expression, case sensitive\n  \"\"\"\n  _nregex: String\n\n  \"\"\"\n  does the column NOT match the given SQL regular expression\n  \"\"\"\n  _nsimilar: String\n\n  \"\"\"\n  does the column match the given POSIX regular expression, case sensitive\n  \"\"\"\n  _regex: String\n\n  \"\"\"\n  does the column match the given SQL regular expression\n  \"\"\"\n  _similar: String\n}\n\n\"\"\"\ncolumns and relationships of \"accounts\"\n\"\"\"\ntype accounts {\n  access_token: String\n  expires_at: Int\n  id: uuid!\n  id_token: String\n  provider: String!\n  providerAccountId: String!\n  refresh_token: String\n  scope: String\n  session_state: String\n  token_type: String\n  type: provider_type_enum!\n\n  \"\"\"\n  An object relationship\n  \"\"\"\n  user: users!\n  userId: uuid!\n}\n\n\"\"\"\naggregated selection of \"accounts\"\n\"\"\"\ntype accounts_aggregate {\n  aggregate: accounts_aggregate_fields\n  nodes: [accounts!]!\n}\n\ninput accounts_aggregate_bool_exp {\n  count: accounts_aggregate_bool_exp_count\n}\n\ninput accounts_aggregate_bool_exp_count {\n  arguments: [accounts_select_column!]\n  distinct: Boolean\n  filter: accounts_bool_exp\n  predicate: Int_comparison_exp!\n}\n\n\"\"\"\naggregate fields of \"accounts\"\n\"\"\"\ntype accounts_aggregate_fields {\n  avg: accounts_avg_fields\n  count(columns: [accounts_select_column!], distinct: Boolean): Int!\n  max: accounts_max_fields\n  min: accounts_min_fields\n  stddev: accounts_stddev_fields\n  stddev_pop: accounts_stddev_pop_fields\n  stddev_samp: accounts_stddev_samp_fields\n  sum: accounts_sum_fields\n  var_pop: accounts_var_pop_fields\n  var_samp: accounts_var_samp_fields\n  variance: accounts_variance_fields\n}\n\n\"\"\"\norder by aggregate values of table \"accounts\"\n\"\"\"\ninput accounts_aggregate_order_by {\n  avg: accounts_avg_order_by\n  count: order_by\n  max: accounts_max_order_by\n  min: accounts_min_order_by\n  stddev: accounts_stddev_order_by\n  stddev_pop: accounts_stddev_pop_order_by\n  stddev_samp: accounts_stddev_samp_order_by\n  sum: accounts_sum_order_by\n  var_pop: accounts_var_pop_order_by\n  var_samp: accounts_var_samp_order_by\n  variance: accounts_variance_order_by\n}\n\n\"\"\"\ninput type for inserting array relation for remote table \"accounts\"\n\"\"\"\ninput accounts_arr_rel_insert_input {\n  data: [accounts_insert_input!]!\n\n  \"\"\"\n  upsert condition\n  \"\"\"\n  on_conflict: accounts_on_conflict\n}\n\n\"\"\"\naggregate avg on columns\n\"\"\"\ntype accounts_avg_fields {\n  expires_at: Float\n}\n\n\"\"\"\norder by avg() on columns of table \"accounts\"\n\"\"\"\ninput accounts_avg_order_by {\n  expires_at: order_by\n}\n\n\"\"\"\nBoolean expression to filter rows from the table \"accounts\". All fields are combined with a logical 'AND'.\n\"\"\"\ninput accounts_bool_exp {\n  _and: [accounts_bool_exp!]\n  _not: accounts_bool_exp\n  _or: [accounts_bool_exp!]\n  access_token: String_comparison_exp\n  expires_at: Int_comparison_exp\n  id: uuid_comparison_exp\n  id_token: String_comparison_exp\n  provider: String_comparison_exp\n  providerAccountId: String_comparison_exp\n  refresh_token: String_comparison_exp\n  scope: String_comparison_exp\n  session_state: String_comparison_exp\n  token_type: String_comparison_exp\n  type: provider_type_enum_comparison_exp\n  user: users_bool_exp\n  userId: uuid_comparison_exp\n}\n\n\"\"\"\nunique or primary key constraints on table \"accounts\"\n\"\"\"\nenum accounts_constraint {\n  \"\"\"\n  unique or primary key constraint on columns \"id\"\n  \"\"\"\n  accounts_pkey\n}\n\n\"\"\"\ninput type for incrementing numeric columns in table \"accounts\"\n\"\"\"\ninput accounts_inc_input {\n  expires_at: Int\n}\n\n\"\"\"\ninput type for inserting data into table \"accounts\"\n\"\"\"\ninput accounts_insert_input {\n  access_token: String\n  expires_at: Int\n  id: uuid\n  id_token: String\n  provider: String\n  providerAccountId: String\n  refresh_token: String\n  scope: String\n  session_state: String\n  token_type: String\n  type: provider_type_enum\n  user: users_obj_rel_insert_input\n  userId: uuid\n}\n\n\"\"\"\naggregate max on columns\n\"\"\"\ntype accounts_max_fields {\n  access_token: String\n  expires_at: Int\n  id: uuid\n  id_token: String\n  provider: String\n  providerAccountId: String\n  refresh_token: String\n  scope: String\n  session_state: String\n  token_type: String\n  userId: uuid\n}\n\n\"\"\"\norder by max() on columns of table \"accounts\"\n\"\"\"\ninput accounts_max_order_by {\n  access_token: order_by\n  expires_at: order_by\n  id: order_by\n  id_token: order_by\n  provider: order_by\n  providerAccountId: order_by\n  refresh_token: order_by\n  scope: order_by\n  session_state: order_by\n  token_type: order_by\n  userId: order_by\n}\n\n\"\"\"\naggregate min on columns\n\"\"\"\ntype accounts_min_fields {\n  access_token: String\n  expires_at: Int\n  id: uuid\n  id_token: String\n  provider: String\n  providerAccountId: String\n  refresh_token: String\n  scope: String\n  session_state: String\n  token_type: String\n  userId: uuid\n}\n\n\"\"\"\norder by min() on columns of table \"accounts\"\n\"\"\"\ninput accounts_min_order_by {\n  access_token: order_by\n  expires_at: order_by\n  id: order_by\n  id_token: order_by\n  provider: order_by\n  providerAccountId: order_by\n  refresh_token: order_by\n  scope: order_by\n  session_state: order_by\n  token_type: order_by\n  userId: order_by\n}\n\n\"\"\"\nresponse of any mutation on the table \"accounts\"\n\"\"\"\ntype accounts_mutation_response {\n  \"\"\"\n  number of rows affected by the mutation\n  \"\"\"\n  affected_rows: Int!\n\n  \"\"\"\n  data from the rows affected by the mutation\n  \"\"\"\n  returning: [accounts!]!\n}\n\n\"\"\"\non_conflict condition type for table \"accounts\"\n\"\"\"\ninput accounts_on_conflict {\n  constraint: accounts_constraint!\n  update_columns: [accounts_update_column!]! = []\n  where: accounts_bool_exp\n}\n\n\"\"\"\nOrdering options when selecting data from \"accounts\".\n\"\"\"\ninput accounts_order_by {\n  access_token: order_by\n  expires_at: order_by\n  id: order_by\n  id_token: order_by\n  provider: order_by\n  providerAccountId: order_by\n  refresh_token: order_by\n  scope: order_by\n  session_state: order_by\n  token_type: order_by\n  type: order_by\n  user: users_order_by\n  userId: order_by\n}\n\n\"\"\"\nprimary key columns input for table: accounts\n\"\"\"\ninput accounts_pk_columns_input {\n  id: uuid!\n}\n\n\"\"\"\nselect columns of table \"accounts\"\n\"\"\"\nenum accounts_select_column {\n  \"\"\"\n  column name\n  \"\"\"\n  access_token\n\n  \"\"\"\n  column name\n  \"\"\"\n  expires_at\n\n  \"\"\"\n  column name\n  \"\"\"\n  id\n\n  \"\"\"\n  column name\n  \"\"\"\n  id_token\n\n  \"\"\"\n  column name\n  \"\"\"\n  provider\n\n  \"\"\"\n  column name\n  \"\"\"\n  providerAccountId\n\n  \"\"\"\n  column name\n  \"\"\"\n  refresh_token\n\n  \"\"\"\n  column name\n  \"\"\"\n  scope\n\n  \"\"\"\n  column name\n  \"\"\"\n  session_state\n\n  \"\"\"\n  column name\n  \"\"\"\n  token_type\n\n  \"\"\"\n  column name\n  \"\"\"\n  type\n\n  \"\"\"\n  column name\n  \"\"\"\n  userId\n}\n\n\"\"\"\ninput type for updating data in table \"accounts\"\n\"\"\"\ninput accounts_set_input {\n  access_token: String\n  expires_at: Int\n  id: uuid\n  id_token: String\n  provider: String\n  providerAccountId: String\n  refresh_token: String\n  scope: String\n  session_state: String\n  token_type: String\n  type: provider_type_enum\n  userId: uuid\n}\n\n\"\"\"\naggregate stddev on columns\n\"\"\"\ntype accounts_stddev_fields {\n  expires_at: Float\n}\n\n\"\"\"\norder by stddev() on columns of table \"accounts\"\n\"\"\"\ninput accounts_stddev_order_by {\n  expires_at: order_by\n}\n\n\"\"\"\naggregate stddev_pop on columns\n\"\"\"\ntype accounts_stddev_pop_fields {\n  expires_at: Float\n}\n\n\"\"\"\norder by stddev_pop() on columns of table \"accounts\"\n\"\"\"\ninput accounts_stddev_pop_order_by {\n  expires_at: order_by\n}\n\n\"\"\"\naggregate stddev_samp on columns\n\"\"\"\ntype accounts_stddev_samp_fields {\n  expires_at: Float\n}\n\n\"\"\"\norder by stddev_samp() on columns of table \"accounts\"\n\"\"\"\ninput accounts_stddev_samp_order_by {\n  expires_at: order_by\n}\n\n\"\"\"\nStreaming cursor of the table \"accounts\"\n\"\"\"\ninput accounts_stream_cursor_input {\n  \"\"\"\n  Stream column input with initial value\n  \"\"\"\n  initial_value: accounts_stream_cursor_value_input!\n\n  \"\"\"\n  cursor ordering\n  \"\"\"\n  ordering: cursor_ordering\n}\n\n\"\"\"\nInitial value of the column from where the streaming should start\n\"\"\"\ninput accounts_stream_cursor_value_input {\n  access_token: String\n  expires_at: Int\n  id: uuid\n  id_token: String\n  provider: String\n  providerAccountId: String\n  refresh_token: String\n  scope: String\n  session_state: String\n  token_type: String\n  type: provider_type_enum\n  userId: uuid\n}\n\n\"\"\"\naggregate sum on columns\n\"\"\"\ntype accounts_sum_fields {\n  expires_at: Int\n}\n\n\"\"\"\norder by sum() on columns of table \"accounts\"\n\"\"\"\ninput accounts_sum_order_by {\n  expires_at: order_by\n}\n\n\"\"\"\nupdate columns of table \"accounts\"\n\"\"\"\nenum accounts_update_column {\n  \"\"\"\n  column name\n  \"\"\"\n  access_token\n\n  \"\"\"\n  column name\n  \"\"\"\n  expires_at\n\n  \"\"\"\n  column name\n  \"\"\"\n  id\n\n  \"\"\"\n  column name\n  \"\"\"\n  id_token\n\n  \"\"\"\n  column name\n  \"\"\"\n  provider\n\n  \"\"\"\n  column name\n  \"\"\"\n  providerAccountId\n\n  \"\"\"\n  column name\n  \"\"\"\n  refresh_token\n\n  \"\"\"\n  column name\n  \"\"\"\n  scope\n\n  \"\"\"\n  column name\n  \"\"\"\n  session_state\n\n  \"\"\"\n  column name\n  \"\"\"\n  token_type\n\n  \"\"\"\n  column name\n  \"\"\"\n  type\n\n  \"\"\"\n  column name\n  \"\"\"\n  userId\n}\n\ninput accounts_updates {\n  \"\"\"\n  increments the numeric columns with given value of the filtered values\n  \"\"\"\n  _inc: accounts_inc_input\n\n  \"\"\"\n  sets the columns of the filtered rows to the given values\n  \"\"\"\n  _set: accounts_set_input\n\n  \"\"\"\n  filter the rows which have to be updated\n  \"\"\"\n  where: accounts_bool_exp!\n}\n\n\"\"\"\naggregate var_pop on columns\n\"\"\"\ntype accounts_var_pop_fields {\n  expires_at: Float\n}\n\n\"\"\"\norder by var_pop() on columns of table \"accounts\"\n\"\"\"\ninput accounts_var_pop_order_by {\n  expires_at: order_by\n}\n\n\"\"\"\naggregate var_samp on columns\n\"\"\"\ntype accounts_var_samp_fields {\n  expires_at: Float\n}\n\n\"\"\"\norder by var_samp() on columns of table \"accounts\"\n\"\"\"\ninput accounts_var_samp_order_by {\n  expires_at: order_by\n}\n\n\"\"\"\naggregate variance on columns\n\"\"\"\ntype accounts_variance_fields {\n  expires_at: Float\n}\n\n\"\"\"\norder by variance() on columns of table \"accounts\"\n\"\"\"\ninput accounts_variance_order_by {\n  expires_at: order_by\n}\n\n\"\"\"\nordering argument of a cursor\n\"\"\"\nenum cursor_ordering {\n  \"\"\"\n  ascending ordering of the cursor\n  \"\"\"\n  ASC\n\n  \"\"\"\n  descending ordering of the cursor\n  \"\"\"\n  DESC\n}\n\n\"\"\"\nmutation root\n\"\"\"\ntype mutation_root {\n  \"\"\"\n  delete data from the table: \"accounts\"\n  \"\"\"\n  delete_accounts(\n    \"\"\"\n    filter the rows which have to be deleted\n    \"\"\"\n    where: accounts_bool_exp!\n  ): accounts_mutation_response\n\n  \"\"\"\n  delete single row from the table: \"accounts\"\n  \"\"\"\n  delete_accounts_by_pk(id: uuid!): accounts\n\n  \"\"\"\n  delete data from the table: \"provider_type\"\n  \"\"\"\n  delete_provider_type(\n    \"\"\"\n    filter the rows which have to be deleted\n    \"\"\"\n    where: provider_type_bool_exp!\n  ): provider_type_mutation_response\n\n  \"\"\"\n  delete single row from the table: \"provider_type\"\n  \"\"\"\n  delete_provider_type_by_pk(value: String!): provider_type\n\n  \"\"\"\n  delete data from the table: \"sessions\"\n  \"\"\"\n  delete_sessions(\n    \"\"\"\n    filter the rows which have to be deleted\n    \"\"\"\n    where: sessions_bool_exp!\n  ): sessions_mutation_response\n\n  \"\"\"\n  delete single row from the table: \"sessions\"\n  \"\"\"\n  delete_sessions_by_pk(sessionToken: String!): sessions\n\n  \"\"\"\n  delete data from the table: \"users\"\n  \"\"\"\n  delete_users(\n    \"\"\"\n    filter the rows which have to be deleted\n    \"\"\"\n    where: users_bool_exp!\n  ): users_mutation_response\n\n  \"\"\"\n  delete single row from the table: \"users\"\n  \"\"\"\n  delete_users_by_pk(id: uuid!): users\n\n  \"\"\"\n  delete data from the table: \"verification_tokens\"\n  \"\"\"\n  delete_verification_tokens(\n    \"\"\"\n    filter the rows which have to be deleted\n    \"\"\"\n    where: verification_tokens_bool_exp!\n  ): verification_tokens_mutation_response\n\n  \"\"\"\n  delete single row from the table: \"verification_tokens\"\n  \"\"\"\n  delete_verification_tokens_by_pk(token: String!): verification_tokens\n\n  \"\"\"\n  insert data into the table: \"accounts\"\n  \"\"\"\n  insert_accounts(\n    \"\"\"\n    the rows to be inserted\n    \"\"\"\n    objects: [accounts_insert_input!]!\n\n    \"\"\"\n    upsert condition\n    \"\"\"\n    on_conflict: accounts_on_conflict\n  ): accounts_mutation_response\n\n  \"\"\"\n  insert a single row into the table: \"accounts\"\n  \"\"\"\n  insert_accounts_one(\n    \"\"\"\n    the row to be inserted\n    \"\"\"\n    object: accounts_insert_input!\n\n    \"\"\"\n    upsert condition\n    \"\"\"\n    on_conflict: accounts_on_conflict\n  ): accounts\n\n  \"\"\"\n  insert data into the table: \"provider_type\"\n  \"\"\"\n  insert_provider_type(\n    \"\"\"\n    the rows to be inserted\n    \"\"\"\n    objects: [provider_type_insert_input!]!\n\n    \"\"\"\n    upsert condition\n    \"\"\"\n    on_conflict: provider_type_on_conflict\n  ): provider_type_mutation_response\n\n  \"\"\"\n  insert a single row into the table: \"provider_type\"\n  \"\"\"\n  insert_provider_type_one(\n    \"\"\"\n    the row to be inserted\n    \"\"\"\n    object: provider_type_insert_input!\n\n    \"\"\"\n    upsert condition\n    \"\"\"\n    on_conflict: provider_type_on_conflict\n  ): provider_type\n\n  \"\"\"\n  insert data into the table: \"sessions\"\n  \"\"\"\n  insert_sessions(\n    \"\"\"\n    the rows to be inserted\n    \"\"\"\n    objects: [sessions_insert_input!]!\n\n    \"\"\"\n    upsert condition\n    \"\"\"\n    on_conflict: sessions_on_conflict\n  ): sessions_mutation_response\n\n  \"\"\"\n  insert a single row into the table: \"sessions\"\n  \"\"\"\n  insert_sessions_one(\n    \"\"\"\n    the row to be inserted\n    \"\"\"\n    object: sessions_insert_input!\n\n    \"\"\"\n    upsert condition\n    \"\"\"\n    on_conflict: sessions_on_conflict\n  ): sessions\n\n  \"\"\"\n  insert data into the table: \"users\"\n  \"\"\"\n  insert_users(\n    \"\"\"\n    the rows to be inserted\n    \"\"\"\n    objects: [users_insert_input!]!\n\n    \"\"\"\n    upsert condition\n    \"\"\"\n    on_conflict: users_on_conflict\n  ): users_mutation_response\n\n  \"\"\"\n  insert a single row into the table: \"users\"\n  \"\"\"\n  insert_users_one(\n    \"\"\"\n    the row to be inserted\n    \"\"\"\n    object: users_insert_input!\n\n    \"\"\"\n    upsert condition\n    \"\"\"\n    on_conflict: users_on_conflict\n  ): users\n\n  \"\"\"\n  insert data into the table: \"verification_tokens\"\n  \"\"\"\n  insert_verification_tokens(\n    \"\"\"\n    the rows to be inserted\n    \"\"\"\n    objects: [verification_tokens_insert_input!]!\n\n    \"\"\"\n    upsert condition\n    \"\"\"\n    on_conflict: verification_tokens_on_conflict\n  ): verification_tokens_mutation_response\n\n  \"\"\"\n  insert a single row into the table: \"verification_tokens\"\n  \"\"\"\n  insert_verification_tokens_one(\n    \"\"\"\n    the row to be inserted\n    \"\"\"\n    object: verification_tokens_insert_input!\n\n    \"\"\"\n    upsert condition\n    \"\"\"\n    on_conflict: verification_tokens_on_conflict\n  ): verification_tokens\n\n  \"\"\"\n  update data of the table: \"accounts\"\n  \"\"\"\n  update_accounts(\n    \"\"\"\n    increments the numeric columns with given value of the filtered values\n    \"\"\"\n    _inc: accounts_inc_input\n\n    \"\"\"\n    sets the columns of the filtered rows to the given values\n    \"\"\"\n    _set: accounts_set_input\n\n    \"\"\"\n    filter the rows which have to be updated\n    \"\"\"\n    where: accounts_bool_exp!\n  ): accounts_mutation_response\n\n  \"\"\"\n  update single row of the table: \"accounts\"\n  \"\"\"\n  update_accounts_by_pk(\n    \"\"\"\n    increments the numeric columns with given value of the filtered values\n    \"\"\"\n    _inc: accounts_inc_input\n\n    \"\"\"\n    sets the columns of the filtered rows to the given values\n    \"\"\"\n    _set: accounts_set_input\n    pk_columns: accounts_pk_columns_input!\n  ): accounts\n\n  \"\"\"\n  update multiples rows of table: \"accounts\"\n  \"\"\"\n  update_accounts_many(\n    \"\"\"\n    updates to execute, in order\n    \"\"\"\n    updates: [accounts_updates!]!\n  ): [accounts_mutation_response]\n\n  \"\"\"\n  update data of the table: \"provider_type\"\n  \"\"\"\n  update_provider_type(\n    \"\"\"\n    sets the columns of the filtered rows to the given values\n    \"\"\"\n    _set: provider_type_set_input\n\n    \"\"\"\n    filter the rows which have to be updated\n    \"\"\"\n    where: provider_type_bool_exp!\n  ): provider_type_mutation_response\n\n  \"\"\"\n  update single row of the table: \"provider_type\"\n  \"\"\"\n  update_provider_type_by_pk(\n    \"\"\"\n    sets the columns of the filtered rows to the given values\n    \"\"\"\n    _set: provider_type_set_input\n    pk_columns: provider_type_pk_columns_input!\n  ): provider_type\n\n  \"\"\"\n  update multiples rows of table: \"provider_type\"\n  \"\"\"\n  update_provider_type_many(\n    \"\"\"\n    updates to execute, in order\n    \"\"\"\n    updates: [provider_type_updates!]!\n  ): [provider_type_mutation_response]\n\n  \"\"\"\n  update data of the table: \"sessions\"\n  \"\"\"\n  update_sessions(\n    \"\"\"\n    sets the columns of the filtered rows to the given values\n    \"\"\"\n    _set: sessions_set_input\n\n    \"\"\"\n    filter the rows which have to be updated\n    \"\"\"\n    where: sessions_bool_exp!\n  ): sessions_mutation_response\n\n  \"\"\"\n  update single row of the table: \"sessions\"\n  \"\"\"\n  update_sessions_by_pk(\n    \"\"\"\n    sets the columns of the filtered rows to the given values\n    \"\"\"\n    _set: sessions_set_input\n    pk_columns: sessions_pk_columns_input!\n  ): sessions\n\n  \"\"\"\n  update multiples rows of table: \"sessions\"\n  \"\"\"\n  update_sessions_many(\n    \"\"\"\n    updates to execute, in order\n    \"\"\"\n    updates: [sessions_updates!]!\n  ): [sessions_mutation_response]\n\n  \"\"\"\n  update data of the table: \"users\"\n  \"\"\"\n  update_users(\n    \"\"\"\n    sets the columns of the filtered rows to the given values\n    \"\"\"\n    _set: users_set_input\n\n    \"\"\"\n    filter the rows which have to be updated\n    \"\"\"\n    where: users_bool_exp!\n  ): users_mutation_response\n\n  \"\"\"\n  update single row of the table: \"users\"\n  \"\"\"\n  update_users_by_pk(\n    \"\"\"\n    sets the columns of the filtered rows to the given values\n    \"\"\"\n    _set: users_set_input\n    pk_columns: users_pk_columns_input!\n  ): users\n\n  \"\"\"\n  update multiples rows of table: \"users\"\n  \"\"\"\n  update_users_many(\n    \"\"\"\n    updates to execute, in order\n    \"\"\"\n    updates: [users_updates!]!\n  ): [users_mutation_response]\n\n  \"\"\"\n  update data of the table: \"verification_tokens\"\n  \"\"\"\n  update_verification_tokens(\n    \"\"\"\n    sets the columns of the filtered rows to the given values\n    \"\"\"\n    _set: verification_tokens_set_input\n\n    \"\"\"\n    filter the rows which have to be updated\n    \"\"\"\n    where: verification_tokens_bool_exp!\n  ): verification_tokens_mutation_response\n\n  \"\"\"\n  update single row of the table: \"verification_tokens\"\n  \"\"\"\n  update_verification_tokens_by_pk(\n    \"\"\"\n    sets the columns of the filtered rows to the given values\n    \"\"\"\n    _set: verification_tokens_set_input\n    pk_columns: verification_tokens_pk_columns_input!\n  ): verification_tokens\n\n  \"\"\"\n  update multiples rows of table: \"verification_tokens\"\n  \"\"\"\n  update_verification_tokens_many(\n    \"\"\"\n    updates to execute, in order\n    \"\"\"\n    updates: [verification_tokens_updates!]!\n  ): [verification_tokens_mutation_response]\n}\n\n\"\"\"\ncolumn ordering options\n\"\"\"\nenum order_by {\n  \"\"\"\n  in ascending order, nulls last\n  \"\"\"\n  asc\n\n  \"\"\"\n  in ascending order, nulls first\n  \"\"\"\n  asc_nulls_first\n\n  \"\"\"\n  in ascending order, nulls last\n  \"\"\"\n  asc_nulls_last\n\n  \"\"\"\n  in descending order, nulls first\n  \"\"\"\n  desc\n\n  \"\"\"\n  in descending order, nulls first\n  \"\"\"\n  desc_nulls_first\n\n  \"\"\"\n  in descending order, nulls last\n  \"\"\"\n  desc_nulls_last\n}\n\n\"\"\"\ncolumns and relationships of \"provider_type\"\n\"\"\"\ntype provider_type {\n  value: String!\n}\n\n\"\"\"\naggregated selection of \"provider_type\"\n\"\"\"\ntype provider_type_aggregate {\n  aggregate: provider_type_aggregate_fields\n  nodes: [provider_type!]!\n}\n\n\"\"\"\naggregate fields of \"provider_type\"\n\"\"\"\ntype provider_type_aggregate_fields {\n  count(columns: [provider_type_select_column!], distinct: Boolean): Int!\n  max: provider_type_max_fields\n  min: provider_type_min_fields\n}\n\n\"\"\"\nBoolean expression to filter rows from the table \"provider_type\". All fields are combined with a logical 'AND'.\n\"\"\"\ninput provider_type_bool_exp {\n  _and: [provider_type_bool_exp!]\n  _not: provider_type_bool_exp\n  _or: [provider_type_bool_exp!]\n  value: String_comparison_exp\n}\n\n\"\"\"\nunique or primary key constraints on table \"provider_type\"\n\"\"\"\nenum provider_type_constraint {\n  \"\"\"\n  unique or primary key constraint on columns \"value\"\n  \"\"\"\n  provider_type_pkey\n}\n\nenum provider_type_enum {\n  credentials\n  email\n  oauth\n  oidc\n  webauthn\n}\n\n\"\"\"\nBoolean expression to compare columns of type \"provider_type_enum\". All fields are combined with logical 'AND'.\n\"\"\"\ninput provider_type_enum_comparison_exp {\n  _eq: provider_type_enum\n  _in: [provider_type_enum!]\n  _is_null: Boolean\n  _neq: provider_type_enum\n  _nin: [provider_type_enum!]\n}\n\n\"\"\"\ninput type for inserting data into table \"provider_type\"\n\"\"\"\ninput provider_type_insert_input {\n  value: String\n}\n\n\"\"\"\naggregate max on columns\n\"\"\"\ntype provider_type_max_fields {\n  value: String\n}\n\n\"\"\"\naggregate min on columns\n\"\"\"\ntype provider_type_min_fields {\n  value: String\n}\n\n\"\"\"\nresponse of any mutation on the table \"provider_type\"\n\"\"\"\ntype provider_type_mutation_response {\n  \"\"\"\n  number of rows affected by the mutation\n  \"\"\"\n  affected_rows: Int!\n\n  \"\"\"\n  data from the rows affected by the mutation\n  \"\"\"\n  returning: [provider_type!]!\n}\n\n\"\"\"\non_conflict condition type for table \"provider_type\"\n\"\"\"\ninput provider_type_on_conflict {\n  constraint: provider_type_constraint!\n  update_columns: [provider_type_update_column!]! = []\n  where: provider_type_bool_exp\n}\n\n\"\"\"\nOrdering options when selecting data from \"provider_type\".\n\"\"\"\ninput provider_type_order_by {\n  value: order_by\n}\n\n\"\"\"\nprimary key columns input for table: provider_type\n\"\"\"\ninput provider_type_pk_columns_input {\n  value: String!\n}\n\n\"\"\"\nselect columns of table \"provider_type\"\n\"\"\"\nenum provider_type_select_column {\n  \"\"\"\n  column name\n  \"\"\"\n  value\n}\n\n\"\"\"\ninput type for updating data in table \"provider_type\"\n\"\"\"\ninput provider_type_set_input {\n  value: String\n}\n\n\"\"\"\nStreaming cursor of the table \"provider_type\"\n\"\"\"\ninput provider_type_stream_cursor_input {\n  \"\"\"\n  Stream column input with initial value\n  \"\"\"\n  initial_value: provider_type_stream_cursor_value_input!\n\n  \"\"\"\n  cursor ordering\n  \"\"\"\n  ordering: cursor_ordering\n}\n\n\"\"\"\nInitial value of the column from where the streaming should start\n\"\"\"\ninput provider_type_stream_cursor_value_input {\n  value: String\n}\n\n\"\"\"\nupdate columns of table \"provider_type\"\n\"\"\"\nenum provider_type_update_column {\n  \"\"\"\n  column name\n  \"\"\"\n  value\n}\n\ninput provider_type_updates {\n  \"\"\"\n  sets the columns of the filtered rows to the given values\n  \"\"\"\n  _set: provider_type_set_input\n\n  \"\"\"\n  filter the rows which have to be updated\n  \"\"\"\n  where: provider_type_bool_exp!\n}\n\ntype query_root {\n  \"\"\"\n  An array relationship\n  \"\"\"\n  accounts(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [accounts_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [accounts_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: accounts_bool_exp\n  ): [accounts!]!\n\n  \"\"\"\n  An aggregate relationship\n  \"\"\"\n  accounts_aggregate(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [accounts_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [accounts_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: accounts_bool_exp\n  ): accounts_aggregate!\n\n  \"\"\"\n  fetch data from the table: \"accounts\" using primary key columns\n  \"\"\"\n  accounts_by_pk(id: uuid!): accounts\n\n  \"\"\"\n  fetch data from the table: \"provider_type\"\n  \"\"\"\n  provider_type(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [provider_type_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [provider_type_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: provider_type_bool_exp\n  ): [provider_type!]!\n\n  \"\"\"\n  fetch aggregated fields from the table: \"provider_type\"\n  \"\"\"\n  provider_type_aggregate(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [provider_type_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [provider_type_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: provider_type_bool_exp\n  ): provider_type_aggregate!\n\n  \"\"\"\n  fetch data from the table: \"provider_type\" using primary key columns\n  \"\"\"\n  provider_type_by_pk(value: String!): provider_type\n\n  \"\"\"\n  An array relationship\n  \"\"\"\n  sessions(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [sessions_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [sessions_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: sessions_bool_exp\n  ): [sessions!]!\n\n  \"\"\"\n  An aggregate relationship\n  \"\"\"\n  sessions_aggregate(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [sessions_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [sessions_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: sessions_bool_exp\n  ): sessions_aggregate!\n\n  \"\"\"\n  fetch data from the table: \"sessions\" using primary key columns\n  \"\"\"\n  sessions_by_pk(sessionToken: String!): sessions\n\n  \"\"\"\n  fetch data from the table: \"users\"\n  \"\"\"\n  users(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [users_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [users_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: users_bool_exp\n  ): [users!]!\n\n  \"\"\"\n  fetch aggregated fields from the table: \"users\"\n  \"\"\"\n  users_aggregate(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [users_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [users_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: users_bool_exp\n  ): users_aggregate!\n\n  \"\"\"\n  fetch data from the table: \"users\" using primary key columns\n  \"\"\"\n  users_by_pk(id: uuid!): users\n\n  \"\"\"\n  fetch data from the table: \"verification_tokens\"\n  \"\"\"\n  verification_tokens(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [verification_tokens_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [verification_tokens_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: verification_tokens_bool_exp\n  ): [verification_tokens!]!\n\n  \"\"\"\n  fetch aggregated fields from the table: \"verification_tokens\"\n  \"\"\"\n  verification_tokens_aggregate(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [verification_tokens_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [verification_tokens_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: verification_tokens_bool_exp\n  ): verification_tokens_aggregate!\n\n  \"\"\"\n  fetch data from the table: \"verification_tokens\" using primary key columns\n  \"\"\"\n  verification_tokens_by_pk(token: String!): verification_tokens\n}\n\n\"\"\"\ncolumns and relationships of \"sessions\"\n\"\"\"\ntype sessions {\n  expires: timestamptz!\n  id: uuid!\n  sessionToken: String!\n\n  \"\"\"\n  An object relationship\n  \"\"\"\n  user: users!\n  userId: uuid!\n}\n\n\"\"\"\naggregated selection of \"sessions\"\n\"\"\"\ntype sessions_aggregate {\n  aggregate: sessions_aggregate_fields\n  nodes: [sessions!]!\n}\n\ninput sessions_aggregate_bool_exp {\n  count: sessions_aggregate_bool_exp_count\n}\n\ninput sessions_aggregate_bool_exp_count {\n  arguments: [sessions_select_column!]\n  distinct: Boolean\n  filter: sessions_bool_exp\n  predicate: Int_comparison_exp!\n}\n\n\"\"\"\naggregate fields of \"sessions\"\n\"\"\"\ntype sessions_aggregate_fields {\n  count(columns: [sessions_select_column!], distinct: Boolean): Int!\n  max: sessions_max_fields\n  min: sessions_min_fields\n}\n\n\"\"\"\norder by aggregate values of table \"sessions\"\n\"\"\"\ninput sessions_aggregate_order_by {\n  count: order_by\n  max: sessions_max_order_by\n  min: sessions_min_order_by\n}\n\n\"\"\"\ninput type for inserting array relation for remote table \"sessions\"\n\"\"\"\ninput sessions_arr_rel_insert_input {\n  data: [sessions_insert_input!]!\n\n  \"\"\"\n  upsert condition\n  \"\"\"\n  on_conflict: sessions_on_conflict\n}\n\n\"\"\"\nBoolean expression to filter rows from the table \"sessions\". All fields are combined with a logical 'AND'.\n\"\"\"\ninput sessions_bool_exp {\n  _and: [sessions_bool_exp!]\n  _not: sessions_bool_exp\n  _or: [sessions_bool_exp!]\n  expires: timestamptz_comparison_exp\n  id: uuid_comparison_exp\n  sessionToken: String_comparison_exp\n  user: users_bool_exp\n  userId: uuid_comparison_exp\n}\n\n\"\"\"\nunique or primary key constraints on table \"sessions\"\n\"\"\"\nenum sessions_constraint {\n  \"\"\"\n  unique or primary key constraint on columns \"sessionToken\"\n  \"\"\"\n  sessions_pkey\n}\n\n\"\"\"\ninput type for inserting data into table \"sessions\"\n\"\"\"\ninput sessions_insert_input {\n  expires: timestamptz\n  id: uuid\n  sessionToken: String\n  user: users_obj_rel_insert_input\n  userId: uuid\n}\n\n\"\"\"\naggregate max on columns\n\"\"\"\ntype sessions_max_fields {\n  expires: timestamptz\n  id: uuid\n  sessionToken: String\n  userId: uuid\n}\n\n\"\"\"\norder by max() on columns of table \"sessions\"\n\"\"\"\ninput sessions_max_order_by {\n  expires: order_by\n  id: order_by\n  sessionToken: order_by\n  userId: order_by\n}\n\n\"\"\"\naggregate min on columns\n\"\"\"\ntype sessions_min_fields {\n  expires: timestamptz\n  id: uuid\n  sessionToken: String\n  userId: uuid\n}\n\n\"\"\"\norder by min() on columns of table \"sessions\"\n\"\"\"\ninput sessions_min_order_by {\n  expires: order_by\n  id: order_by\n  sessionToken: order_by\n  userId: order_by\n}\n\n\"\"\"\nresponse of any mutation on the table \"sessions\"\n\"\"\"\ntype sessions_mutation_response {\n  \"\"\"\n  number of rows affected by the mutation\n  \"\"\"\n  affected_rows: Int!\n\n  \"\"\"\n  data from the rows affected by the mutation\n  \"\"\"\n  returning: [sessions!]!\n}\n\n\"\"\"\non_conflict condition type for table \"sessions\"\n\"\"\"\ninput sessions_on_conflict {\n  constraint: sessions_constraint!\n  update_columns: [sessions_update_column!]! = []\n  where: sessions_bool_exp\n}\n\n\"\"\"\nOrdering options when selecting data from \"sessions\".\n\"\"\"\ninput sessions_order_by {\n  expires: order_by\n  id: order_by\n  sessionToken: order_by\n  user: users_order_by\n  userId: order_by\n}\n\n\"\"\"\nprimary key columns input for table: sessions\n\"\"\"\ninput sessions_pk_columns_input {\n  sessionToken: String!\n}\n\n\"\"\"\nselect columns of table \"sessions\"\n\"\"\"\nenum sessions_select_column {\n  \"\"\"\n  column name\n  \"\"\"\n  expires\n\n  \"\"\"\n  column name\n  \"\"\"\n  id\n\n  \"\"\"\n  column name\n  \"\"\"\n  sessionToken\n\n  \"\"\"\n  column name\n  \"\"\"\n  userId\n}\n\n\"\"\"\ninput type for updating data in table \"sessions\"\n\"\"\"\ninput sessions_set_input {\n  expires: timestamptz\n  id: uuid\n  sessionToken: String\n  userId: uuid\n}\n\n\"\"\"\nStreaming cursor of the table \"sessions\"\n\"\"\"\ninput sessions_stream_cursor_input {\n  \"\"\"\n  Stream column input with initial value\n  \"\"\"\n  initial_value: sessions_stream_cursor_value_input!\n\n  \"\"\"\n  cursor ordering\n  \"\"\"\n  ordering: cursor_ordering\n}\n\n\"\"\"\nInitial value of the column from where the streaming should start\n\"\"\"\ninput sessions_stream_cursor_value_input {\n  expires: timestamptz\n  id: uuid\n  sessionToken: String\n  userId: uuid\n}\n\n\"\"\"\nupdate columns of table \"sessions\"\n\"\"\"\nenum sessions_update_column {\n  \"\"\"\n  column name\n  \"\"\"\n  expires\n\n  \"\"\"\n  column name\n  \"\"\"\n  id\n\n  \"\"\"\n  column name\n  \"\"\"\n  sessionToken\n\n  \"\"\"\n  column name\n  \"\"\"\n  userId\n}\n\ninput sessions_updates {\n  \"\"\"\n  sets the columns of the filtered rows to the given values\n  \"\"\"\n  _set: sessions_set_input\n\n  \"\"\"\n  filter the rows which have to be updated\n  \"\"\"\n  where: sessions_bool_exp!\n}\n\ntype subscription_root {\n  \"\"\"\n  An array relationship\n  \"\"\"\n  accounts(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [accounts_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [accounts_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: accounts_bool_exp\n  ): [accounts!]!\n\n  \"\"\"\n  An aggregate relationship\n  \"\"\"\n  accounts_aggregate(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [accounts_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [accounts_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: accounts_bool_exp\n  ): accounts_aggregate!\n\n  \"\"\"\n  fetch data from the table: \"accounts\" using primary key columns\n  \"\"\"\n  accounts_by_pk(id: uuid!): accounts\n\n  \"\"\"\n  fetch data from the table in a streaming manner: \"accounts\"\n  \"\"\"\n  accounts_stream(\n    \"\"\"\n    maximum number of rows returned in a single batch\n    \"\"\"\n    batch_size: Int!\n\n    \"\"\"\n    cursor to stream the results returned by the query\n    \"\"\"\n    cursor: [accounts_stream_cursor_input]!\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: accounts_bool_exp\n  ): [accounts!]!\n\n  \"\"\"\n  fetch data from the table: \"provider_type\"\n  \"\"\"\n  provider_type(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [provider_type_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [provider_type_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: provider_type_bool_exp\n  ): [provider_type!]!\n\n  \"\"\"\n  fetch aggregated fields from the table: \"provider_type\"\n  \"\"\"\n  provider_type_aggregate(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [provider_type_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [provider_type_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: provider_type_bool_exp\n  ): provider_type_aggregate!\n\n  \"\"\"\n  fetch data from the table: \"provider_type\" using primary key columns\n  \"\"\"\n  provider_type_by_pk(value: String!): provider_type\n\n  \"\"\"\n  fetch data from the table in a streaming manner: \"provider_type\"\n  \"\"\"\n  provider_type_stream(\n    \"\"\"\n    maximum number of rows returned in a single batch\n    \"\"\"\n    batch_size: Int!\n\n    \"\"\"\n    cursor to stream the results returned by the query\n    \"\"\"\n    cursor: [provider_type_stream_cursor_input]!\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: provider_type_bool_exp\n  ): [provider_type!]!\n\n  \"\"\"\n  An array relationship\n  \"\"\"\n  sessions(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [sessions_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [sessions_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: sessions_bool_exp\n  ): [sessions!]!\n\n  \"\"\"\n  An aggregate relationship\n  \"\"\"\n  sessions_aggregate(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [sessions_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [sessions_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: sessions_bool_exp\n  ): sessions_aggregate!\n\n  \"\"\"\n  fetch data from the table: \"sessions\" using primary key columns\n  \"\"\"\n  sessions_by_pk(sessionToken: String!): sessions\n\n  \"\"\"\n  fetch data from the table in a streaming manner: \"sessions\"\n  \"\"\"\n  sessions_stream(\n    \"\"\"\n    maximum number of rows returned in a single batch\n    \"\"\"\n    batch_size: Int!\n\n    \"\"\"\n    cursor to stream the results returned by the query\n    \"\"\"\n    cursor: [sessions_stream_cursor_input]!\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: sessions_bool_exp\n  ): [sessions!]!\n\n  \"\"\"\n  fetch data from the table: \"users\"\n  \"\"\"\n  users(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [users_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [users_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: users_bool_exp\n  ): [users!]!\n\n  \"\"\"\n  fetch aggregated fields from the table: \"users\"\n  \"\"\"\n  users_aggregate(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [users_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [users_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: users_bool_exp\n  ): users_aggregate!\n\n  \"\"\"\n  fetch data from the table: \"users\" using primary key columns\n  \"\"\"\n  users_by_pk(id: uuid!): users\n\n  \"\"\"\n  fetch data from the table in a streaming manner: \"users\"\n  \"\"\"\n  users_stream(\n    \"\"\"\n    maximum number of rows returned in a single batch\n    \"\"\"\n    batch_size: Int!\n\n    \"\"\"\n    cursor to stream the results returned by the query\n    \"\"\"\n    cursor: [users_stream_cursor_input]!\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: users_bool_exp\n  ): [users!]!\n\n  \"\"\"\n  fetch data from the table: \"verification_tokens\"\n  \"\"\"\n  verification_tokens(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [verification_tokens_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [verification_tokens_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: verification_tokens_bool_exp\n  ): [verification_tokens!]!\n\n  \"\"\"\n  fetch aggregated fields from the table: \"verification_tokens\"\n  \"\"\"\n  verification_tokens_aggregate(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [verification_tokens_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [verification_tokens_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: verification_tokens_bool_exp\n  ): verification_tokens_aggregate!\n\n  \"\"\"\n  fetch data from the table: \"verification_tokens\" using primary key columns\n  \"\"\"\n  verification_tokens_by_pk(token: String!): verification_tokens\n\n  \"\"\"\n  fetch data from the table in a streaming manner: \"verification_tokens\"\n  \"\"\"\n  verification_tokens_stream(\n    \"\"\"\n    maximum number of rows returned in a single batch\n    \"\"\"\n    batch_size: Int!\n\n    \"\"\"\n    cursor to stream the results returned by the query\n    \"\"\"\n    cursor: [verification_tokens_stream_cursor_input]!\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: verification_tokens_bool_exp\n  ): [verification_tokens!]!\n}\n\nscalar timestamptz\n\n\"\"\"\nBoolean expression to compare columns of type \"timestamptz\". All fields are combined with logical 'AND'.\n\"\"\"\ninput timestamptz_comparison_exp {\n  _eq: timestamptz\n  _gt: timestamptz\n  _gte: timestamptz\n  _in: [timestamptz!]\n  _is_null: Boolean\n  _lt: timestamptz\n  _lte: timestamptz\n  _neq: timestamptz\n  _nin: [timestamptz!]\n}\n\n\"\"\"\ncolumns and relationships of \"users\"\n\"\"\"\ntype users {\n  \"\"\"\n  An array relationship\n  \"\"\"\n  accounts(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [accounts_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [accounts_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: accounts_bool_exp\n  ): [accounts!]!\n\n  \"\"\"\n  An aggregate relationship\n  \"\"\"\n  accounts_aggregate(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [accounts_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [accounts_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: accounts_bool_exp\n  ): accounts_aggregate!\n  email: String!\n  emailVerified: timestamptz\n  id: uuid!\n  image: String\n  name: String\n\n  \"\"\"\n  An array relationship\n  \"\"\"\n  sessions(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [sessions_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [sessions_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: sessions_bool_exp\n  ): [sessions!]!\n\n  \"\"\"\n  An aggregate relationship\n  \"\"\"\n  sessions_aggregate(\n    \"\"\"\n    distinct select on columns\n    \"\"\"\n    distinct_on: [sessions_select_column!]\n\n    \"\"\"\n    limit the number of rows returned\n    \"\"\"\n    limit: Int\n\n    \"\"\"\n    skip the first n rows. Use only with order_by\n    \"\"\"\n    offset: Int\n\n    \"\"\"\n    sort the rows by one or more columns\n    \"\"\"\n    order_by: [sessions_order_by!]\n\n    \"\"\"\n    filter the rows returned\n    \"\"\"\n    where: sessions_bool_exp\n  ): sessions_aggregate!\n}\n\n\"\"\"\naggregated selection of \"users\"\n\"\"\"\ntype users_aggregate {\n  aggregate: users_aggregate_fields\n  nodes: [users!]!\n}\n\n\"\"\"\naggregate fields of \"users\"\n\"\"\"\ntype users_aggregate_fields {\n  count(columns: [users_select_column!], distinct: Boolean): Int!\n  max: users_max_fields\n  min: users_min_fields\n}\n\n\"\"\"\nBoolean expression to filter rows from the table \"users\". All fields are combined with a logical 'AND'.\n\"\"\"\ninput users_bool_exp {\n  _and: [users_bool_exp!]\n  _not: users_bool_exp\n  _or: [users_bool_exp!]\n  accounts: accounts_bool_exp\n  accounts_aggregate: accounts_aggregate_bool_exp\n  email: String_comparison_exp\n  emailVerified: timestamptz_comparison_exp\n  id: uuid_comparison_exp\n  image: String_comparison_exp\n  name: String_comparison_exp\n  sessions: sessions_bool_exp\n  sessions_aggregate: sessions_aggregate_bool_exp\n}\n\n\"\"\"\nunique or primary key constraints on table \"users\"\n\"\"\"\nenum users_constraint {\n  \"\"\"\n  unique or primary key constraint on columns \"email\"\n  \"\"\"\n  users_email_key\n\n  \"\"\"\n  unique or primary key constraint on columns \"id\"\n  \"\"\"\n  users_pkey\n}\n\n\"\"\"\ninput type for inserting data into table \"users\"\n\"\"\"\ninput users_insert_input {\n  accounts: accounts_arr_rel_insert_input\n  email: String\n  emailVerified: timestamptz\n  id: uuid\n  image: String\n  name: String\n  sessions: sessions_arr_rel_insert_input\n}\n\n\"\"\"\naggregate max on columns\n\"\"\"\ntype users_max_fields {\n  email: String\n  emailVerified: timestamptz\n  id: uuid\n  image: String\n  name: String\n}\n\n\"\"\"\naggregate min on columns\n\"\"\"\ntype users_min_fields {\n  email: String\n  emailVerified: timestamptz\n  id: uuid\n  image: String\n  name: String\n}\n\n\"\"\"\nresponse of any mutation on the table \"users\"\n\"\"\"\ntype users_mutation_response {\n  \"\"\"\n  number of rows affected by the mutation\n  \"\"\"\n  affected_rows: Int!\n\n  \"\"\"\n  data from the rows affected by the mutation\n  \"\"\"\n  returning: [users!]!\n}\n\n\"\"\"\ninput type for inserting object relation for remote table \"users\"\n\"\"\"\ninput users_obj_rel_insert_input {\n  data: users_insert_input!\n\n  \"\"\"\n  upsert condition\n  \"\"\"\n  on_conflict: users_on_conflict\n}\n\n\"\"\"\non_conflict condition type for table \"users\"\n\"\"\"\ninput users_on_conflict {\n  constraint: users_constraint!\n  update_columns: [users_update_column!]! = []\n  where: users_bool_exp\n}\n\n\"\"\"\nOrdering options when selecting data from \"users\".\n\"\"\"\ninput users_order_by {\n  accounts_aggregate: accounts_aggregate_order_by\n  email: order_by\n  emailVerified: order_by\n  id: order_by\n  image: order_by\n  name: order_by\n  sessions_aggregate: sessions_aggregate_order_by\n}\n\n\"\"\"\nprimary key columns input for table: users\n\"\"\"\ninput users_pk_columns_input {\n  id: uuid!\n}\n\n\"\"\"\nselect columns of table \"users\"\n\"\"\"\nenum users_select_column {\n  \"\"\"\n  column name\n  \"\"\"\n  email\n\n  \"\"\"\n  column name\n  \"\"\"\n  emailVerified\n\n  \"\"\"\n  column name\n  \"\"\"\n  id\n\n  \"\"\"\n  column name\n  \"\"\"\n  image\n\n  \"\"\"\n  column name\n  \"\"\"\n  name\n}\n\n\"\"\"\ninput type for updating data in table \"users\"\n\"\"\"\ninput users_set_input {\n  email: String\n  emailVerified: timestamptz\n  id: uuid\n  image: String\n  name: String\n}\n\n\"\"\"\nStreaming cursor of the table \"users\"\n\"\"\"\ninput users_stream_cursor_input {\n  \"\"\"\n  Stream column input with initial value\n  \"\"\"\n  initial_value: users_stream_cursor_value_input!\n\n  \"\"\"\n  cursor ordering\n  \"\"\"\n  ordering: cursor_ordering\n}\n\n\"\"\"\nInitial value of the column from where the streaming should start\n\"\"\"\ninput users_stream_cursor_value_input {\n  email: String\n  emailVerified: timestamptz\n  id: uuid\n  image: String\n  name: String\n}\n\n\"\"\"\nupdate columns of table \"users\"\n\"\"\"\nenum users_update_column {\n  \"\"\"\n  column name\n  \"\"\"\n  email\n\n  \"\"\"\n  column name\n  \"\"\"\n  emailVerified\n\n  \"\"\"\n  column name\n  \"\"\"\n  id\n\n  \"\"\"\n  column name\n  \"\"\"\n  image\n\n  \"\"\"\n  column name\n  \"\"\"\n  name\n}\n\ninput users_updates {\n  \"\"\"\n  sets the columns of the filtered rows to the given values\n  \"\"\"\n  _set: users_set_input\n\n  \"\"\"\n  filter the rows which have to be updated\n  \"\"\"\n  where: users_bool_exp!\n}\n\nscalar uuid\n\n\"\"\"\nBoolean expression to compare columns of type \"uuid\". All fields are combined with logical 'AND'.\n\"\"\"\ninput uuid_comparison_exp {\n  _eq: uuid\n  _gt: uuid\n  _gte: uuid\n  _in: [uuid!]\n  _is_null: Boolean\n  _lt: uuid\n  _lte: uuid\n  _neq: uuid\n  _nin: [uuid!]\n}\n\n\"\"\"\ncolumns and relationships of \"verification_tokens\"\n\"\"\"\ntype verification_tokens {\n  expires: timestamptz!\n  identifier: String!\n  token: String!\n}\n\n\"\"\"\naggregated selection of \"verification_tokens\"\n\"\"\"\ntype verification_tokens_aggregate {\n  aggregate: verification_tokens_aggregate_fields\n  nodes: [verification_tokens!]!\n}\n\n\"\"\"\naggregate fields of \"verification_tokens\"\n\"\"\"\ntype verification_tokens_aggregate_fields {\n  count(columns: [verification_tokens_select_column!], distinct: Boolean): Int!\n  max: verification_tokens_max_fields\n  min: verification_tokens_min_fields\n}\n\n\"\"\"\nBoolean expression to filter rows from the table \"verification_tokens\". All fields are combined with a logical 'AND'.\n\"\"\"\ninput verification_tokens_bool_exp {\n  _and: [verification_tokens_bool_exp!]\n  _not: verification_tokens_bool_exp\n  _or: [verification_tokens_bool_exp!]\n  expires: timestamptz_comparison_exp\n  identifier: String_comparison_exp\n  token: String_comparison_exp\n}\n\n\"\"\"\nunique or primary key constraints on table \"verification_tokens\"\n\"\"\"\nenum verification_tokens_constraint {\n  \"\"\"\n  unique or primary key constraint on columns \"token\"\n  \"\"\"\n  verification_tokens_pkey\n}\n\n\"\"\"\ninput type for inserting data into table \"verification_tokens\"\n\"\"\"\ninput verification_tokens_insert_input {\n  expires: timestamptz\n  identifier: String\n  token: String\n}\n\n\"\"\"\naggregate max on columns\n\"\"\"\ntype verification_tokens_max_fields {\n  expires: timestamptz\n  identifier: String\n  token: String\n}\n\n\"\"\"\naggregate min on columns\n\"\"\"\ntype verification_tokens_min_fields {\n  expires: timestamptz\n  identifier: String\n  token: String\n}\n\n\"\"\"\nresponse of any mutation on the table \"verification_tokens\"\n\"\"\"\ntype verification_tokens_mutation_response {\n  \"\"\"\n  number of rows affected by the mutation\n  \"\"\"\n  affected_rows: Int!\n\n  \"\"\"\n  data from the rows affected by the mutation\n  \"\"\"\n  returning: [verification_tokens!]!\n}\n\n\"\"\"\non_conflict condition type for table \"verification_tokens\"\n\"\"\"\ninput verification_tokens_on_conflict {\n  constraint: verification_tokens_constraint!\n  update_columns: [verification_tokens_update_column!]! = []\n  where: verification_tokens_bool_exp\n}\n\n\"\"\"\nOrdering options when selecting data from \"verification_tokens\".\n\"\"\"\ninput verification_tokens_order_by {\n  expires: order_by\n  identifier: order_by\n  token: order_by\n}\n\n\"\"\"\nprimary key columns input for table: verification_tokens\n\"\"\"\ninput verification_tokens_pk_columns_input {\n  token: String!\n}\n\n\"\"\"\nselect columns of table \"verification_tokens\"\n\"\"\"\nenum verification_tokens_select_column {\n  \"\"\"\n  column name\n  \"\"\"\n  expires\n\n  \"\"\"\n  column name\n  \"\"\"\n  identifier\n\n  \"\"\"\n  column name\n  \"\"\"\n  token\n}\n\n\"\"\"\ninput type for updating data in table \"verification_tokens\"\n\"\"\"\ninput verification_tokens_set_input {\n  expires: timestamptz\n  identifier: String\n  token: String\n}\n\n\"\"\"\nStreaming cursor of the table \"verification_tokens\"\n\"\"\"\ninput verification_tokens_stream_cursor_input {\n  \"\"\"\n  Stream column input with initial value\n  \"\"\"\n  initial_value: verification_tokens_stream_cursor_value_input!\n\n  \"\"\"\n  cursor ordering\n  \"\"\"\n  ordering: cursor_ordering\n}\n\n\"\"\"\nInitial value of the column from where the streaming should start\n\"\"\"\ninput verification_tokens_stream_cursor_value_input {\n  expires: timestamptz\n  identifier: String\n  token: String\n}\n\n\"\"\"\nupdate columns of table \"verification_tokens\"\n\"\"\"\nenum verification_tokens_update_column {\n  \"\"\"\n  column name\n  \"\"\"\n  expires\n\n  \"\"\"\n  column name\n  \"\"\"\n  identifier\n\n  \"\"\"\n  column name\n  \"\"\"\n  token\n}\n\ninput verification_tokens_updates {\n  \"\"\"\n  sets the columns of the filtered rows to the given values\n  \"\"\"\n  _set: verification_tokens_set_input\n\n  \"\"\"\n  filter the rows which have to be updated\n  \"\"\"\n  where: verification_tokens_bool_exp!\n}\n"
  },
  {
    "path": "packages/adapter-hasura/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: \"16px\"}}>\n *  <p>Official <a href=\"https://hasura.io/\">Hasura</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://hasura.io/\">\n *   <img style={{display: \"block\"}} src=\"/img/adapters/hasura.svg\" width=\"38\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/hasura-adapter\n * ```\n *\n * @module @auth/hasura-adapter\n */\n\nimport { isDate, type Adapter } from \"@auth/core/adapters\"\n\nimport {\n  client as hasuraClient,\n  type HasuraAdapterClient,\n} from \"./lib/client.js\"\nimport { useFragment } from \"./lib/generated/index.js\"\nimport {\n  AccountFragmentDoc,\n  CreateAccountDocument,\n  CreateSessionDocument,\n  CreateUserDocument,\n  CreateVerificationTokenDocument,\n  DeleteAccountDocument,\n  DeleteSessionDocument,\n  DeleteUserDocument,\n  DeleteVerificationTokenDocument,\n  GetSessionAndUserDocument,\n  GetUserDocument,\n  GetUsersDocument,\n  SessionFragmentDoc,\n  UpdateSessionDocument,\n  UpdateUserDocument,\n  UserFragmentDoc,\n  VerificationTokenFragmentDoc,\n} from \"./lib/generated/graphql.js\"\n\nexport function HasuraAdapter(client: HasuraAdapterClient): Adapter {\n  const c = hasuraClient(client)\n\n  return {\n    async createUser(newUser) {\n      const { insert_users_one } = await c.run(CreateUserDocument, {\n        data: format.to<any>(newUser),\n      })\n\n      return format.from(useFragment(UserFragmentDoc, insert_users_one), true)\n    },\n    async getUser(id) {\n      const { users_by_pk } = await c.run(GetUserDocument, { id })\n\n      return format.from(useFragment(UserFragmentDoc, users_by_pk))\n    },\n    async getUserByEmail(email) {\n      const { users } = await c.run(GetUsersDocument, {\n        where: { email: { _eq: email } },\n      })\n\n      return format.from(useFragment(UserFragmentDoc, users?.[0]))\n    },\n    async getUserByAccount({ providerAccountId, provider }) {\n      const { users } = await c.run(GetUsersDocument, {\n        where: {\n          accounts: {\n            provider: { _eq: provider },\n            providerAccountId: { _eq: providerAccountId },\n          },\n        },\n      })\n\n      return format.from(useFragment(UserFragmentDoc, users?.[0]))\n    },\n    async updateUser({ id, ...data }) {\n      const { update_users_by_pk } = await c.run(UpdateUserDocument, {\n        id,\n        data: format.to<any>(data),\n      })\n\n      return format.from(useFragment(UserFragmentDoc, update_users_by_pk), true)\n    },\n    async deleteUser(id) {\n      const { delete_users_by_pk } = await c.run(DeleteUserDocument, { id })\n\n      return format.from<any, true>(\n        useFragment(UserFragmentDoc, delete_users_by_pk),\n        true\n      )\n    },\n    async createSession(data) {\n      const { insert_sessions_one } = await c.run(CreateSessionDocument, {\n        data: format.to<any>(data),\n      })\n\n      return format.from(\n        useFragment(SessionFragmentDoc, insert_sessions_one),\n        true\n      )\n    },\n    async getSessionAndUser(sessionToken) {\n      const { sessions } = await c.run(GetSessionAndUserDocument, {\n        sessionToken,\n      })\n      const sessionAndUser = sessions?.[0]\n      if (!sessionAndUser) return null\n\n      const { user, ...session } = sessionAndUser\n\n      return {\n        session: format.from(useFragment(SessionFragmentDoc, session), true),\n        user: format.from(useFragment(UserFragmentDoc, user), true),\n      }\n    },\n    async updateSession({ sessionToken, ...data }) {\n      const { update_sessions } = await c.run(UpdateSessionDocument, {\n        sessionToken,\n        data: format.to<any>(data),\n      })\n      const session = update_sessions?.returning?.[0]\n\n      return format.from(useFragment(SessionFragmentDoc, session))\n    },\n    async deleteSession(sessionToken) {\n      const { delete_sessions } = await c.run(DeleteSessionDocument, {\n        sessionToken,\n      })\n      const session = delete_sessions?.returning?.[0]\n\n      return format.from<any>(useFragment(SessionFragmentDoc, session))\n    },\n    async linkAccount(data) {\n      const { insert_accounts_one } = await c.run(CreateAccountDocument, {\n        data,\n      })\n\n      return useFragment(AccountFragmentDoc, insert_accounts_one) as any\n    },\n    async unlinkAccount(params) {\n      const { delete_accounts } = await c.run(DeleteAccountDocument, params)\n      const account = delete_accounts?.returning[0]\n\n      return useFragment(AccountFragmentDoc, account) as any\n    },\n    async createVerificationToken(data) {\n      const { insert_verification_tokens_one } = await c.run(\n        CreateVerificationTokenDocument,\n        { data: format.to<any>(data) }\n      )\n\n      return format.from(\n        useFragment(\n          VerificationTokenFragmentDoc,\n          insert_verification_tokens_one\n        )\n      )\n    },\n    async useVerificationToken(params) {\n      const { delete_verification_tokens } = await c.run(\n        DeleteVerificationTokenDocument,\n        params\n      )\n      const verificationToken = delete_verification_tokens?.returning?.[0]\n\n      return format.from(\n        useFragment(VerificationTokenFragmentDoc, verificationToken)\n      )\n    },\n  }\n}\n\nexport const format = {\n  from<T, B extends boolean = false>(\n    object?: Record<string, any> | null | undefined,\n    throwIfNullish?: B\n  ): B extends true ? T : T | null {\n    if (!object) {\n      if (throwIfNullish) throw new Error(\"Object is nullish\")\n      return null as any\n    }\n\n    const newObject: Record<string, unknown> = {}\n\n    for (const [key, value] of Object.entries(object))\n      newObject[key] = isDate(value) ? new Date(value) : value\n\n    return newObject as T\n  },\n  to<T>(object: Record<string, any>): T {\n    const newObject: Record<string, unknown> = {}\n\n    for (const [key, value] of Object.entries(object))\n      newObject[key] = value instanceof Date ? value.toISOString() : value\n\n    return newObject as T\n  },\n}\n"
  },
  {
    "path": "packages/adapter-hasura/src/lib/client.ts",
    "content": "import type { TypedDocumentString } from \"./generated/graphql.js\"\n\nexport interface HasuraAdapterClient {\n  endpoint: string\n  /**\n   * `x-hasura-admin-secret` header value\n   *\n   * [Hasura Authentication](https://hasura.io/docs/search/?q=x-hasura-admin-secret)\n   */\n  adminSecret: string\n}\n\nexport class HasuraClientError extends Error {\n  name = \"HasuraClientError\"\n  constructor(\n    errors: any[],\n    query: TypedDocumentString<any, any>,\n    variables: any\n  ) {\n    super(errors.map((error) => error.message).join(\"\\n\"))\n    console.error({ query, variables })\n  }\n}\n\nexport function client({ adminSecret, endpoint }: HasuraAdapterClient) {\n  if (!adminSecret)\n    throw new TypeError(\"Hasura client error: Please provide an adminSecret\")\n\n  if (!endpoint)\n    throw new TypeError(\n      \"Hasura client error: Please provide a graphql endpoint\"\n    )\n\n  return {\n    async run<\n      Q extends TypedDocumentString<any, any>,\n      T extends Q extends TypedDocumentString<infer T, any> ? T : never,\n      V extends Q extends TypedDocumentString<any, infer V> ? V : never,\n    >(query: Q, variables?: V): Promise<T> {\n      const response = await fetch(endpoint, {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n          \"x-hasura-admin-secret\": adminSecret,\n        },\n        body: JSON.stringify({ query, variables }),\n      })\n\n      const { data = {}, errors } = await response.json()\n\n      if (errors?.length) throw new HasuraClientError(errors, query, variables)\n\n      return data as T\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-hasura/src/queries/account.graphql",
    "content": "mutation CreateAccount($data: accounts_insert_input!) {\n  insert_accounts_one(object: $data) {\n    ...Account\n  }\n}\n\nmutation DeleteAccount($provider: String!, $providerAccountId: String!) {\n  delete_accounts(\n    where: {\n      provider: { _eq: $provider }\n      providerAccountId: { _eq: $providerAccountId }\n    }\n  ) {\n    returning {\n      ...Account\n    }\n  }\n}\n\nquery GetAccount($provider: String!, $providerAccountId: String!) {\n  accounts(\n    where: {\n      provider: { _eq: $provider }\n      providerAccountId: { _eq: $providerAccountId }\n    }\n  ) {\n    ...Account\n  }\n}\n"
  },
  {
    "path": "packages/adapter-hasura/src/queries/delete.graphql",
    "content": "mutation DeleteAll {\n  delete_accounts(where: {}) {\n    affected_rows\n  }\n  delete_sessions(where: {}) {\n    affected_rows\n  }\n  delete_users(where: {}) {\n    affected_rows\n  }\n  delete_verification_tokens(where: {}) {\n    affected_rows\n  }\n}\n"
  },
  {
    "path": "packages/adapter-hasura/src/queries/fragments.graphql",
    "content": "fragment User on users {\n  id\n  name\n  email\n  image\n  emailVerified\n}\n\nfragment Session on sessions {\n  id\n  userId\n  expires\n  sessionToken\n}\n\nfragment Account on accounts {\n  id\n  type\n  scope\n  userId\n  id_token\n  provider\n  expires_at\n  token_type\n  access_token\n  refresh_token\n  session_state\n  providerAccountId\n}\n\nfragment VerificationToken on verification_tokens {\n  token\n  expires\n  identifier\n}\n"
  },
  {
    "path": "packages/adapter-hasura/src/queries/session.graphql",
    "content": "query GetSessionAndUser($sessionToken: String!) {\n  sessions(where: { sessionToken: { _eq: $sessionToken } }) {\n    ...Session\n    user {\n      ...User\n    }\n  }\n}\n\nquery GetSession($sessionToken: String!) {\n  sessions_by_pk(sessionToken: $sessionToken) {\n    ...Session\n  }\n}\n\nmutation CreateSession($data: sessions_insert_input!) {\n  insert_sessions_one(object: $data) {\n    ...Session\n  }\n}\n\nmutation UpdateSession($sessionToken: String, $data: sessions_set_input!) {\n  update_sessions(\n    where: { sessionToken: { _eq: $sessionToken } }\n    _set: $data\n  ) {\n    returning {\n      ...Session\n    }\n  }\n}\n\nmutation DeleteSession($sessionToken: String!) {\n  delete_sessions(where: { sessionToken: { _eq: $sessionToken } }) {\n    returning {\n      ...Session\n    }\n  }\n}\n"
  },
  {
    "path": "packages/adapter-hasura/src/queries/user.graphql",
    "content": "query GetUser($id: uuid!) {\n  users_by_pk(id: $id) {\n    ...User\n  }\n}\n\nquery GetUsers($where: users_bool_exp!) {\n  users(where: $where) {\n    ...User\n  }\n}\n\nmutation CreateUser($data: users_insert_input!) {\n  insert_users_one(object: $data) {\n    ...User\n  }\n}\n\nmutation UpdateUser($id: uuid!, $data: users_set_input!) {\n  update_users_by_pk(pk_columns: { id: $id }, _set: $data) {\n    ...User\n  }\n}\n\nmutation DeleteUser($id: uuid!) {\n  delete_users_by_pk(id: $id) {\n    ...User\n  }\n}\n"
  },
  {
    "path": "packages/adapter-hasura/src/queries/verification-token.graphql",
    "content": "mutation CreateVerificationToken($data: verification_tokens_insert_input!) {\n  insert_verification_tokens_one(object: $data) {\n    ...VerificationToken\n  }\n}\n\nmutation DeleteVerificationToken($identifier: String!, $token: String!) {\n  delete_verification_tokens(\n    where: { token: { _eq: $token }, identifier: { _eq: $identifier } }\n  ) {\n    returning {\n      ...VerificationToken\n    }\n  }\n}\n\nquery GetVerificationToken($identifier: String!, $token: String!) {\n  verification_tokens(\n    where: { token: { _eq: $token }, identifier: { _eq: $identifier } }\n  ) {\n    ...VerificationToken\n  }\n}\n"
  },
  {
    "path": "packages/adapter-hasura/test/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { HasuraAdapter, format } from \"../src\"\nimport { useFragment } from \"../src/lib/generated\"\nimport {\n  AccountFragmentDoc,\n  DeleteAllDocument,\n  GetAccountDocument,\n  GetSessionDocument,\n  GetUserDocument,\n  GetVerificationTokenDocument,\n  SessionFragmentDoc,\n  UserFragmentDoc,\n  VerificationTokenFragmentDoc,\n} from \"../src/lib/generated/graphql\"\nimport { client as hasuraClient } from \"../src/lib/client\"\n\nconst client = hasuraClient({\n  endpoint: \"http://localhost:8080/v1/graphql\",\n  adminSecret: \"myadminsecretkey\",\n})\n\nrunBasicTests({\n  adapter: HasuraAdapter({\n    adminSecret: \"myadminsecretkey\",\n    endpoint: \"http://localhost:8080/v1/graphql\",\n  }),\n  db: {\n    async connect() {\n      await client.run(DeleteAllDocument)\n    },\n    async disconnect() {\n      await client.run(DeleteAllDocument)\n    },\n    async user(id) {\n      const { users_by_pk } = await client.run(GetUserDocument, { id })\n      const user = useFragment(UserFragmentDoc, users_by_pk)\n      return format.from(user)\n    },\n    async account(params) {\n      const { accounts } = await client.run(GetAccountDocument, params)\n\n      return useFragment(AccountFragmentDoc, accounts?.[0]) ?? null\n    },\n    async session(sessionToken) {\n      const { sessions_by_pk } = await client.run(GetSessionDocument, {\n        sessionToken,\n      })\n\n      return format.from(useFragment(SessionFragmentDoc, sessions_by_pk))\n    },\n    async verificationToken(params) {\n      const { verification_tokens } = await client.run(\n        GetVerificationTokenDocument,\n        params\n      )\n      const verificationToken = verification_tokens?.[0]\n\n      return format.from(\n        useFragment(VerificationTokenFragmentDoc, verificationToken)\n      )\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-hasura/test/test.sh",
    "content": "#!/usr/bin/env bash\n\n# Start Hasura\ndocker compose up -d\n\necho \"Waiting 5s for db to start...\"\nsleep 5\n\n# Always stop container, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  docker compose down -v\nelse\n  docker compose down -v && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-hasura/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-hasura/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\", \"src/lib/client.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/hasura-adapter\",\n  entryFileName: \"../hasura-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-kysely/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://kysely.io\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/kysely.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Kysely Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/kysely-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/kysely-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/kysely-adapter?color=green&label=@auth/kysely-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/kysely-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/kysely-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/kysely).\n"
  },
  {
    "path": "packages/adapter-kysely/package.json",
    "content": "{\n  \"name\": \"@auth/kysely-adapter\",\n  \"version\": \"1.11.0\",\n  \"description\": \"Kysely adapter for Auth.js\",\n  \"homepage\": \"https://authjs.dev/reference/adapter/kysely\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"mwojtul <mark.wojtul@gmail.com> (https://github.com/mwojtul)\",\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"authjs\",\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"kysely\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"scripts\": {\n    \"build\": \"tsc\",\n    \"test\": \"./test/test.sh\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"kysely\": \"^0.27.5\"\n  },\n  \"devDependencies\": {\n    \"@types/pg\": \"^8.6.5\",\n    \"kysely\": \"^0.27.5\",\n    \"libsql\": \"^0.3.18\",\n    \"mysql2\": \"^3.9.7\",\n    \"pg\": \"^8.10.0\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-kysely/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>Official <a href=\"https://kysely.dev/\">Kysely</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://kysely.dev/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/kysely.svg\" width=\"30\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/kysely-adapter kysely\n * ```\n *\n * @module @auth/kysely-adapter\n */\nimport { Kysely, SqliteAdapter } from \"kysely\"\n\nimport {\n  type Adapter,\n  type AdapterUser,\n  type AdapterAccount,\n  type AdapterSession,\n  type VerificationToken,\n  isDate,\n} from \"@auth/core/adapters\"\n\nexport interface Database {\n  User: AdapterUser\n  Account: AdapterAccount\n  Session: AdapterSession\n  VerificationToken: VerificationToken\n}\n\nexport const format = {\n  from<T>(object?: Record<string, any>): T {\n    const newObject: Record<string, unknown> = {}\n    for (const key in object) {\n      const value = object[key]\n      if (isDate(value)) newObject[key] = new Date(value)\n      else newObject[key] = value\n    }\n    return newObject as T\n  },\n  to<T>(object: Record<string, any>): T {\n    const newObject: Record<string, unknown> = {}\n    for (const [key, value] of Object.entries(object))\n      newObject[key] = value instanceof Date ? value.toISOString() : value\n    return newObject as T\n  },\n}\n\nexport function KyselyAdapter(db: Kysely<Database>): Adapter {\n  const { adapter } = db.getExecutor()\n  const { supportsReturning } = adapter\n  const isSqlite = adapter instanceof SqliteAdapter\n  /** If the database is SQLite, turn dates into an ISO string  */\n  const to = isSqlite ? format.to : <T>(x: T) => x as T\n  /** If the database is SQLite, turn ISO strings into dates */\n  const from = isSqlite ? format.from : <T>(x: T) => x as T\n  return {\n    async createUser(data) {\n      const user = { ...data, id: crypto.randomUUID() }\n      await db.insertInto(\"User\").values(to(user)).executeTakeFirstOrThrow()\n      return user\n    },\n    async getUser(id) {\n      const result = await db\n        .selectFrom(\"User\")\n        .selectAll()\n        .where(\"id\", \"=\", id)\n        .executeTakeFirst()\n      if (!result) return null\n      return from(result)\n    },\n    async getUserByEmail(email) {\n      const result = await db\n        .selectFrom(\"User\")\n        .selectAll()\n        .where(\"email\", \"=\", email)\n        .executeTakeFirst()\n      if (!result) return null\n      return from(result)\n    },\n    async getUserByAccount({ providerAccountId, provider }) {\n      const result = await db\n        .selectFrom(\"User\")\n        .innerJoin(\"Account\", \"User.id\", \"Account.userId\")\n        .selectAll(\"User\")\n        .where(\"Account.providerAccountId\", \"=\", providerAccountId)\n        .where(\"Account.provider\", \"=\", provider)\n        .executeTakeFirst()\n      if (!result) return null\n      return from(result)\n    },\n    async updateUser({ id, ...user }) {\n      const userData = to(user)\n      const query = db.updateTable(\"User\").set(userData).where(\"id\", \"=\", id)\n      const result = supportsReturning\n        ? query.returningAll().executeTakeFirstOrThrow()\n        : query\n            .executeTakeFirstOrThrow()\n            .then(() =>\n              db\n                .selectFrom(\"User\")\n                .selectAll()\n                .where(\"id\", \"=\", id)\n                .executeTakeFirstOrThrow()\n            )\n      return from(await result)\n    },\n    async deleteUser(userId) {\n      await db\n        .deleteFrom(\"User\")\n        .where(\"User.id\", \"=\", userId)\n        .executeTakeFirst()\n    },\n    async linkAccount(account) {\n      await db\n        .insertInto(\"Account\")\n        .values(to(account))\n        .executeTakeFirstOrThrow()\n      return account\n    },\n    async unlinkAccount({ providerAccountId, provider }) {\n      await db\n        .deleteFrom(\"Account\")\n        .where(\"Account.providerAccountId\", \"=\", providerAccountId)\n        .where(\"Account.provider\", \"=\", provider)\n        .executeTakeFirstOrThrow()\n    },\n    async createSession(session) {\n      await db.insertInto(\"Session\").values(to(session)).execute()\n      return session\n    },\n    async getSessionAndUser(sessionToken) {\n      const result = await db\n        .selectFrom(\"Session\")\n        .innerJoin(\"User\", \"User.id\", \"Session.userId\")\n        .selectAll(\"User\")\n        .select([\"Session.expires\", \"Session.userId\"])\n        .where(\"Session.sessionToken\", \"=\", sessionToken)\n        .executeTakeFirst()\n      if (!result) return null\n      const { userId, expires, ...user } = result\n      const session = { sessionToken, userId, expires }\n      return { user: from(user), session: from(session) }\n    },\n    async updateSession(session) {\n      const sessionData = to(session)\n      const query = db\n        .updateTable(\"Session\")\n        .set(sessionData)\n        .where(\"Session.sessionToken\", \"=\", session.sessionToken)\n      const result = supportsReturning\n        ? await query.returningAll().executeTakeFirstOrThrow()\n        : await query.executeTakeFirstOrThrow().then(async () => {\n            return await db\n              .selectFrom(\"Session\")\n              .selectAll()\n              .where(\"Session.sessionToken\", \"=\", sessionData.sessionToken)\n              .executeTakeFirstOrThrow()\n          })\n      return from(result)\n    },\n    async deleteSession(sessionToken) {\n      await db\n        .deleteFrom(\"Session\")\n        .where(\"Session.sessionToken\", \"=\", sessionToken)\n        .executeTakeFirstOrThrow()\n    },\n    async createVerificationToken(data) {\n      await db.insertInto(\"VerificationToken\").values(to(data)).execute()\n      return data\n    },\n    async useVerificationToken({ identifier, token }) {\n      const query = db\n        .deleteFrom(\"VerificationToken\")\n        .where(\"VerificationToken.token\", \"=\", token)\n        .where(\"VerificationToken.identifier\", \"=\", identifier)\n\n      const result = supportsReturning\n        ? await query.returningAll().executeTakeFirst()\n        : await db\n            .selectFrom(\"VerificationToken\")\n            .selectAll()\n            .where(\"token\", \"=\", token)\n            .where(\"identifier\", \"=\", identifier)\n            .executeTakeFirst()\n            .then(async (res) => {\n              await query.executeTakeFirst()\n              return res\n            })\n      if (!result) return null\n      return from(result)\n    },\n  }\n}\n\n/**\n * Wrapper over the original `Kysely` class in order to validate the passed in\n * database interface. A regular Kysely instance may also be used, but wrapping\n * it ensures the database interface implements the fields that Auth.js\n * requires. When used with `kysely-codegen`, the `Codegen` type can be passed as\n * the second generic argument. The generated types will be used, and\n * `KyselyAuth` will only verify that the correct fields exist.\n * @noInheritDoc\n */\nexport class KyselyAuth<DB extends T, T = Database> extends Kysely<DB> {}\n\nexport type Codegen = {\n  [K in keyof Database]: { [J in keyof Database[K]]: unknown }\n}\n"
  },
  {
    "path": "packages/adapter-kysely/test/index.test.ts",
    "content": "import { describe } from \"vitest\"\nimport { runBasicTests } from \"utils/adapter\"\nimport { Pool } from \"pg\"\nimport {\n  MysqlDialect,\n  PostgresDialect,\n  SchemaModule,\n  sql,\n  SqliteAdapter,\n  SqliteDialect,\n} from \"kysely\"\nimport { createPool } from \"mysql2\"\nimport SqliteDatabase from \"libsql\"\nimport { KyselyAdapter, KyselyAuth } from \"../src\"\nimport type { Database } from \"../src\"\n\nexport function createTableWithId(\n  schema: SchemaModule,\n  dialect: string,\n  tableName: string\n) {\n  const builder = schema.createTable(tableName)\n  switch (dialect) {\n    case \"postgres\":\n      return builder.addColumn(\"id\", \"uuid\", (col) =>\n        col.primaryKey().defaultTo(sql`gen_random_uuid()`)\n      )\n    case \"mysql\":\n      return builder.addColumn(\"id\", \"varchar(36)\", (col) =>\n        col.primaryKey().defaultTo(sql`(UUID())`)\n      )\n    case \"sqlite\":\n      return builder.addColumn(\"id\", \"varchar\", (col) => col.primaryKey())\n    default:\n      throw new Error(\"Unsupported dialect\")\n  }\n}\n\ndescribe.each([\n  new PostgresDialect({\n    pool: new Pool({\n      host: \"localhost\",\n      database: \"kysely_test\",\n      user: \"kysely\",\n      port: 5434,\n      max: 20,\n    }),\n  }),\n  new MysqlDialect({\n    pool: createPool({\n      database: \"kysely_test\",\n      host: \"localhost\",\n      user: \"kysely\",\n      password: \"kysely\",\n      port: 3308,\n      supportBigNumbers: true,\n      bigNumberStrings: true,\n      connectionLimit: 20,\n    }),\n  }),\n  new SqliteDialect({\n    database: async () => new SqliteDatabase(\":memory:\"),\n  }),\n])(\"Testing %s dialect\", (dialect) => {\n  const db = new KyselyAuth<Database>({ dialect })\n\n  const { adapter } = db.getExecutor()\n  const isSqlite = adapter instanceof SqliteAdapter\n  const isMysql = dialect instanceof MysqlDialect\n\n  runBasicTests({\n    adapter: KyselyAdapter(db),\n    db: {\n      async connect() {\n        await Promise.all([\n          db.schema.dropTable(\"Account\").ifExists().execute(),\n          db.schema.dropTable(\"Session\").ifExists().execute(),\n          db.schema.dropTable(\"User\").ifExists().execute(),\n          db.schema.dropTable(\"VerificationToken\").ifExists().execute(),\n        ])\n\n        const dialect = isSqlite ? \"sqlite\" : isMysql ? \"mysql\" : \"postgres\"\n        const defaultTimestamp = {\n          postgres: sql`NOW()`,\n          mysql: sql`NOW(3)`,\n          sqlite: sql`CURRENT_TIMESTAMP`,\n        }[dialect]\n        const uuidColumnType = dialect === \"mysql\" ? \"varchar(36)\" : \"uuid\"\n        const dateColumnType =\n          dialect === \"mysql\" ? sql`DATETIME(3)` : \"timestamptz\"\n        const textColumnType = dialect === \"mysql\" ? \"varchar(255)\" : \"text\"\n\n        await createTableWithId(db.schema, dialect, \"User\")\n          .addColumn(\"name\", textColumnType)\n          .addColumn(\"email\", textColumnType, (col) => col.unique().notNull())\n          .addColumn(\"emailVerified\", dateColumnType, (col) =>\n            col.defaultTo(defaultTimestamp)\n          )\n          .addColumn(\"image\", textColumnType)\n          .execute()\n\n        let createAccountTable = db.schema\n          .createTable(\"Account\")\n          .addColumn(\"userId\", uuidColumnType, (col) =>\n            col.references(\"User.id\").onDelete(\"cascade\").notNull()\n          )\n          .addColumn(\"type\", textColumnType, (col) => col.notNull())\n          .addColumn(\"provider\", textColumnType, (col) => col.notNull())\n          .addColumn(\"providerAccountId\", textColumnType, (col) =>\n            col.notNull()\n          )\n          .addColumn(\"refresh_token\", textColumnType)\n          .addColumn(\"access_token\", textColumnType)\n          .addColumn(\"expires_at\", \"bigint\")\n          .addColumn(\"token_type\", textColumnType)\n          .addColumn(\"scope\", textColumnType)\n          .addColumn(\"id_token\", textColumnType)\n          .addColumn(\"session_state\", textColumnType)\n        if (dialect === \"mysql\")\n          createAccountTable = createAccountTable.addForeignKeyConstraint(\n            \"Account_userId_fk\",\n            [\"userId\"],\n            \"User\",\n            [\"id\"],\n            (cb) => cb.onDelete(\"cascade\")\n          )\n        await createAccountTable.execute()\n\n        let createSessionTable = db.schema\n          .createTable(\"Session\")\n          .addColumn(\"userId\", uuidColumnType, (col) =>\n            col.references(\"User.id\").onDelete(\"cascade\").notNull()\n          )\n          .addColumn(\"sessionToken\", textColumnType, (col) =>\n            col.notNull().unique()\n          )\n          .addColumn(\"expires\", dateColumnType, (col) => col.notNull())\n\n        if (dialect === \"mysql\")\n          createSessionTable = createSessionTable.addForeignKeyConstraint(\n            \"Session_userId_fk\",\n            [\"userId\"],\n            \"User\",\n            [\"id\"],\n            (cb) => cb.onDelete(\"cascade\")\n          )\n\n        await createSessionTable.execute()\n\n        await db.schema\n          .createTable(\"VerificationToken\")\n          .addColumn(\"identifier\", textColumnType, (col) => col.notNull())\n          .addColumn(\"token\", textColumnType, (col) => col.notNull().unique())\n          .addColumn(\"expires\", dateColumnType, (col) => col.notNull())\n          .execute()\n\n        await db.schema\n          .createIndex(\"Account_userId_index\")\n          .on(\"Account\")\n          .column(\"userId\")\n          .execute()\n      },\n      async disconnect() {\n        await db.destroy()\n      },\n      async user(userId) {\n        const user = await db\n          .selectFrom(\"User\")\n          .selectAll()\n          .where(\"id\", \"=\", userId)\n          .executeTakeFirst()\n        if (isSqlite && user?.emailVerified)\n          user.emailVerified = new Date(user.emailVerified)\n        return user ?? null\n      },\n      async account({ provider, providerAccountId }) {\n        const result = await db\n          .selectFrom(\"Account\")\n          .selectAll()\n          .where(\"provider\", \"=\", provider)\n          .where(\"providerAccountId\", \"=\", providerAccountId)\n          .executeTakeFirst()\n        if (!result) return null\n        const { ...account } = result\n        if (typeof account.expires_at === \"string\")\n          account.expires_at = Number(account.expires_at)\n        return account ?? null\n      },\n      async session(sessionToken) {\n        const session = await db\n          .selectFrom(\"Session\")\n          .selectAll()\n          .where(\"sessionToken\", \"=\", sessionToken)\n          .executeTakeFirst()\n        if (isSqlite && session?.expires)\n          session.expires = new Date(session.expires)\n        return session ?? null\n      },\n      async verificationToken({ identifier, token }) {\n        const verificationToken = await db\n          .selectFrom(\"VerificationToken\")\n          .selectAll()\n          .where(\"identifier\", \"=\", identifier)\n          .where(\"token\", \"=\", token)\n          .executeTakeFirstOrThrow()\n        if (isSqlite)\n          verificationToken.expires = new Date(verificationToken.expires)\n        return verificationToken ?? null\n      },\n    },\n  })\n})\n"
  },
  {
    "path": "packages/adapter-kysely/test/scripts/mysql-init.sql",
    "content": "CREATE USER 'kysely'@'%' IDENTIFIED WITH mysql_native_password BY 'kysely';\nGRANT ALL ON *.* TO 'kysely'@'%';\nCREATE DATABASE kysely_test;"
  },
  {
    "path": "packages/adapter-kysely/test/test.sh",
    "content": "#!/usr/bin/env bash\n\nMYSQL_CONTAINER_NAME=authjs-kysely-mysql-test\nPG_CONTAINER_NAME=authjs-kysely-pg-test\n\ndocker run -d --rm \\\n  --name ${MYSQL_CONTAINER_NAME} \\\n  -e MYSQL_ROOT_PASSWORD=root \\\n  -e MYSQL_DATABASE=kysely_test \\\n  -p 3308:3306 \\\n  -v \"$(pwd)\"/test/scripts/mysql-init.sql:/data/application/init.sql \\\n  mysql/mysql-server \\\n  --init-file /data/application/init.sql\n\ndocker run -d --rm \\\n  --name ${PG_CONTAINER_NAME} \\\n  -e POSTGRES_DB=kysely_test \\\n  -e POSTGRES_USER=kysely \\\n  -e POSTGRES_HOST_AUTH_METHOD=trust \\\n  -p 5434:5432 \\\n  postgres\n\necho \"waiting 10s for db to start...\"\nsleep 10\n\n# Always stop container, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  docker stop ${MYSQL_CONTAINER_NAME} && docker stop ${PG_CONTAINER_NAME}\nelse\n  docker stop ${MYSQL_CONTAINER_NAME} && docker stop ${PG_CONTAINER_NAME} && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-kysely/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-kysely/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/kysely-adapter\",\n  entryFileName: \"../kysely-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-mikro-orm/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://mikro-orm.io\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/mikro-orm.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>MikroORM Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/mikro-orm-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/mikro-orm-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/mikro-orm-adapter?color=green&label=@auth/mikro-orm-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/mikro-orm-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/mikro-orm-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/mikro-orm).\n"
  },
  {
    "path": "packages/adapter-mikro-orm/package.json",
    "content": "{\n  \"name\": \"@auth/mikro-orm-adapter\",\n  \"version\": \"2.11.0\",\n  \"description\": \"MikroORM adapter for Auth.js\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Jonas Strassel <info@jonas-strassel.de>\",\n  \"contributors\": [\n    \"Balázs Orbán <info@balazsorban.com>\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"lib\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\",\n      \"default\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"mikro-orm\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"test\": \"vitest -c ../utils/vitest.config.ts\",\n    \"build\": \"tsc\",\n    \"clean\": \"rm -rf *.js *.d.ts* lib\"\n  },\n  \"peerDependencies\": {\n    \"@mikro-orm/core\": \"^5\"\n  },\n  \"devDependencies\": {\n    \"@mikro-orm/core\": \"^5\",\n    \"@mikro-orm/sqlite\": \"^5\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-mikro-orm/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>Official <a href=\"https://mikro-orm.io/docs/installation\">MikroORM</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://mikro-orm.io/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/mikro-orm.svg\" width=\"64\"/>\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @mikro-orm/core @auth/mikro-orm-adapter\n * ```\n *\n * @module @auth/mikro-orm-adapter\n */\nimport type {\n  Connection,\n  IDatabaseDriver,\n  Options as ORMOptions,\n} from \"@mikro-orm/core\"\n\nimport type { Adapter } from \"@auth/core/adapters\"\n\nimport { MikroORM, wrap } from \"@mikro-orm/core\"\n\nimport * as defaultEntities from \"./lib/entities.js\"\n\nexport { defaultEntities }\n\nexport function MikroOrmAdapter<\n  D extends IDatabaseDriver<Connection> = IDatabaseDriver<Connection>,\n>(\n  ormOptions: ORMOptions<D>,\n  options?: {\n    entities?: Partial<typeof defaultEntities>\n  }\n): Adapter {\n  const UserModel = options?.entities?.User ?? defaultEntities.User\n  const AccountModel = options?.entities?.Account ?? defaultEntities.Account\n  const SessionModel = options?.entities?.Session ?? defaultEntities.Session\n  const VerificationTokenModel =\n    options?.entities?.VerificationToken ?? defaultEntities.VerificationToken\n\n  let _orm: MikroORM\n\n  const getEM = async () => {\n    if (!_orm) {\n      // filter out default entities from the passed entities\n      const optionsEntities =\n        ormOptions.entities?.filter((e) => {\n          if (\n            typeof e !== \"string\" &&\n            \"name\" in e &&\n            typeof e.name === \"string\"\n          )\n            return ![\n              \"User\",\n              \"Account\",\n              \"Session\",\n              \"VerificationToken\",\n            ].includes(e.name)\n          return true\n        }) ?? []\n      // add the (un-)enhanced entities to the connection\n      ormOptions.entities = [\n        ...optionsEntities,\n        UserModel,\n        AccountModel,\n        SessionModel,\n        VerificationTokenModel,\n      ]\n      _orm = await MikroORM.init(ormOptions)\n    }\n    return _orm.em.fork()\n  }\n\n  return {\n    /**\n     * Method used in testing. You won't need to call this in your app.\n     * @internal\n     */\n    async __disconnect() {\n      const em = await getEM()\n      await em.getConnection().close()\n    },\n    async createUser(data) {\n      const em = await getEM()\n      const user = new UserModel()\n      wrap(user).assign(data)\n      await em.persistAndFlush(user)\n\n      return wrap(user).toObject()\n    },\n    async getUser(id) {\n      const em = await getEM()\n      const user = await em.findOne(UserModel, { id })\n      if (!user) return null\n\n      return wrap(user).toObject()\n    },\n    async getUserByEmail(email) {\n      const em = await getEM()\n      const user = await em.findOne(UserModel, { email })\n      if (!user) return null\n\n      return wrap(user).toObject()\n    },\n    async getUserByAccount(provider_providerAccountId) {\n      const em = await getEM()\n      const account = await em.findOne(AccountModel, {\n        ...provider_providerAccountId,\n      })\n      if (!account) return null\n      const user = await em.findOne(UserModel, { id: account.userId })\n\n      return wrap(user).toObject()\n    },\n    async updateUser(data) {\n      const em = await getEM()\n      const user = await em.findOne(UserModel, { id: data.id })\n      if (!user) throw new Error(\"User not found\")\n      wrap(user).assign(data, { mergeObjects: true })\n      await em.persistAndFlush(user)\n\n      return wrap(user).toObject()\n    },\n    async deleteUser(id) {\n      const em = await getEM()\n      const user = await em.findOne(UserModel, { id })\n      if (!user) return null\n      await em.removeAndFlush(user)\n\n      return wrap(user).toObject()\n    },\n    // @ts-expect-error\n    async linkAccount(data) {\n      const em = await getEM()\n      const user = await em.findOne(UserModel, { id: data.userId })\n      if (!user) throw new Error(\"User not found\")\n      const account = new AccountModel()\n      wrap(account).assign(data)\n      user.accounts.add(account)\n      await em.persistAndFlush(user)\n\n      return wrap(account).toObject()\n    },\n    // @ts-expect-error\n    async unlinkAccount(provider_providerAccountId) {\n      const em = await getEM()\n      const account = await em.findOne(AccountModel, {\n        ...provider_providerAccountId,\n      })\n      if (!account) throw new Error(\"Account not found\")\n      await em.removeAndFlush(account)\n\n      return wrap(account).toObject()\n    },\n    async getSessionAndUser(sessionToken) {\n      const em = await getEM()\n      const session = await em.findOne(\n        SessionModel,\n        { sessionToken },\n        { populate: [\"user\"] }\n      )\n      if (!session || !session.user) return null\n\n      return {\n        user: wrap(session.user).toObject(),\n        session: wrap(session).toObject(),\n      }\n    },\n    async createSession(data) {\n      const em = await getEM()\n      const user = await em.findOne(UserModel, { id: data.userId })\n      if (!user) throw new Error(\"User not found\")\n      const session = new SessionModel()\n      wrap(session).assign(data)\n      user.sessions.add(session)\n      await em.persistAndFlush(user)\n\n      return wrap(session).toObject()\n    },\n    async updateSession(data) {\n      const em = await getEM()\n      const session = await em.findOne(SessionModel, {\n        sessionToken: data.sessionToken,\n      })\n      wrap(session).assign(data)\n      if (!session) throw new Error(\"Session not found\")\n      await em.persistAndFlush(session)\n\n      return wrap(session).toObject()\n    },\n    async deleteSession(sessionToken) {\n      const em = await getEM()\n      const session = await em.findOne(SessionModel, {\n        sessionToken,\n      })\n      if (!session) return null\n      await em.removeAndFlush(session)\n\n      return wrap(session).toObject()\n    },\n    async createVerificationToken(data) {\n      const em = await getEM()\n      const verificationToken = new VerificationTokenModel()\n      wrap(verificationToken).assign(data)\n      await em.persistAndFlush(verificationToken)\n\n      return wrap(verificationToken).toObject()\n    },\n    async useVerificationToken(params) {\n      const em = await getEM()\n      const verificationToken = await em\n        .getRepository(VerificationTokenModel)\n        .findOne(params)\n      if (!verificationToken) return null\n      await em.removeAndFlush(verificationToken)\n\n      return wrap(verificationToken).toObject()\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-mikro-orm/src/lib/entities.ts",
    "content": "import {\n  Property,\n  Unique,\n  PrimaryKey,\n  Entity,\n  OneToMany,\n  Collection,\n  ManyToOne,\n  types,\n} from \"@mikro-orm/core\"\n\nimport type {\n  AdapterUser,\n  AdapterAccount,\n  AdapterSession,\n  VerificationToken as AdapterVerificationToken,\n} from \"@auth/core/adapters\"\n\ntype RemoveIndex<T> = {\n  // eslint-disable-next-line\n  [K in keyof T as {} extends Record<K, 1> ? never : K]: T[K]\n}\n\n@Entity()\nexport class User implements RemoveIndex<AdapterUser> {\n  @PrimaryKey()\n  id: string = crypto.randomUUID()\n\n  @Property({ type: types.string, nullable: true })\n  name?: AdapterUser[\"name\"]\n\n  @Property({ type: types.string, nullable: true })\n  @Unique()\n  email: AdapterUser[\"email\"] = \"\"\n\n  @Property({ type: types.datetime, nullable: true })\n  emailVerified: AdapterUser[\"emailVerified\"] = null\n\n  @Property({ type: types.string, nullable: true })\n  image?: AdapterUser[\"image\"]\n\n  @OneToMany({\n    entity: \"Session\",\n    mappedBy: (session: Session) => session.user,\n    hidden: true,\n    orphanRemoval: true,\n  })\n  sessions = new Collection<Session, object>(this)\n\n  @OneToMany({\n    entity: \"Account\",\n    mappedBy: (account: Account) => account.user,\n    hidden: true,\n    orphanRemoval: true,\n  })\n  accounts = new Collection<Account, object>(this)\n}\n\n@Entity()\nexport class Session implements AdapterSession {\n  @PrimaryKey()\n  @Property({ type: types.string })\n  id: string = crypto.randomUUID()\n\n  @ManyToOne({\n    entity: \"User\",\n    hidden: true,\n    onDelete: \"cascade\",\n  })\n  user!: User\n\n  @Property({ type: types.string, persist: false })\n  userId!: AdapterSession[\"userId\"]\n\n  @Property({ type: \"Date\" })\n  expires!: AdapterSession[\"expires\"]\n\n  @Property({ type: types.string })\n  @Unique()\n  sessionToken!: AdapterSession[\"sessionToken\"]\n}\n\n@Entity()\n@Unique({ properties: [\"provider\", \"providerAccountId\"] })\nexport class Account implements RemoveIndex<AdapterAccount> {\n  @PrimaryKey()\n  @Property({ type: types.string })\n  id: string = crypto.randomUUID()\n\n  @ManyToOne({\n    entity: \"User\",\n    hidden: true,\n    onDelete: \"cascade\",\n  })\n  user!: User\n\n  @Property({ type: types.string, persist: false })\n  userId!: AdapterAccount[\"userId\"]\n\n  @Property({ type: types.string })\n  type!: AdapterAccount[\"type\"]\n\n  @Property({ type: types.string })\n  provider!: AdapterAccount[\"provider\"]\n\n  @Property({ type: types.string })\n  providerAccountId!: AdapterAccount[\"providerAccountId\"]\n\n  @Property({ type: types.string, nullable: true })\n  refresh_token?: AdapterAccount[\"refresh_token\"]\n\n  @Property({ type: types.string, nullable: true })\n  access_token?: AdapterAccount[\"access_token\"]\n\n  @Property({ type: types.integer, nullable: true })\n  expires_at?: AdapterAccount[\"expires_at\"]\n\n  @Property({ type: types.string, nullable: true })\n  token_type?: AdapterAccount[\"token_type\"]\n\n  @Property({ type: types.string, nullable: true })\n  scope?: AdapterAccount[\"scope\"]\n\n  @Property({ type: types.text, nullable: true })\n  id_token?: AdapterAccount[\"id_token\"]\n\n  @Property({ type: types.string, nullable: true })\n  session_state?: AdapterAccount[\"session_state\"]\n}\n\n@Entity()\n@Unique({ properties: [\"token\", \"identifier\"] })\nexport class VerificationToken implements AdapterVerificationToken {\n  @PrimaryKey()\n  @Property({ type: types.string })\n  token!: AdapterVerificationToken[\"token\"]\n\n  @Property({ type: \"Date\" })\n  expires!: AdapterVerificationToken[\"expires\"]\n\n  @Property({ type: types.string })\n  identifier!: AdapterVerificationToken[\"identifier\"]\n}\n"
  },
  {
    "path": "packages/adapter-mikro-orm/test/__snapshots__/schema.test.ts.snap",
    "content": "// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html\n\nexports[`run migrations > createSchemaSQL 1`] = `\n\"pragma foreign_keys = off;\n\ncreate table \\`user\\` (\\`id\\` text not null, \\`name\\` text null, \\`email\\` text null default '', \\`email_verified\\` datetime null, \\`image\\` text null, primary key (\\`id\\`));\ncreate unique index \\`user_email_unique\\` on \\`user\\` (\\`email\\`);\n\ncreate table \\`session\\` (\\`id\\` text not null, \\`user_id\\` text not null, \\`expires\\` datetime not null, \\`session_token\\` text not null, constraint \\`session_user_id_foreign\\` foreign key(\\`user_id\\`) references \\`user\\`(\\`id\\`) on delete cascade on update cascade, primary key (\\`id\\`));\ncreate index \\`session_user_id_index\\` on \\`session\\` (\\`user_id\\`);\ncreate unique index \\`session_session_token_unique\\` on \\`session\\` (\\`session_token\\`);\n\ncreate table \\`account\\` (\\`id\\` text not null, \\`user_id\\` text not null, \\`type\\` text not null, \\`provider\\` text not null, \\`provider_account_id\\` text not null, \\`refresh_token\\` text null, \\`access_token\\` text null, \\`expires_at\\` integer null, \\`token_type\\` text null, \\`scope\\` text null, \\`id_token\\` text null, \\`session_state\\` text null, constraint \\`account_user_id_foreign\\` foreign key(\\`user_id\\`) references \\`user\\`(\\`id\\`) on delete cascade on update cascade, primary key (\\`id\\`));\ncreate index \\`account_user_id_index\\` on \\`account\\` (\\`user_id\\`);\ncreate unique index \\`account_provider_provider_account_id_unique\\` on \\`account\\` (\\`provider\\`, \\`provider_account_id\\`);\n\ncreate table \\`verification_token\\` (\\`token\\` text not null, \\`expires\\` datetime not null, \\`identifier\\` text not null, primary key (\\`token\\`));\ncreate unique index \\`verification_token_token_identifier_unique\\` on \\`verification_token\\` (\\`token\\`, \\`identifier\\`);\n\npragma foreign_keys = on;\n\"\n`;\n\nexports[`run migrations > targetSchema 1`] = `\n{\n  \"name\": undefined,\n  \"namespaces\": [],\n  \"tables\": [\n    {\n      \"checks\": [],\n      \"columns\": {\n        \"email\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": \"''\",\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"email\",\n          \"nullable\": true,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"email_verified\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": 0,\n          \"mappedType\": \"datetime\",\n          \"name\": \"email_verified\",\n          \"nullable\": true,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"datetime\",\n          \"unsigned\": false,\n        },\n        \"id\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"id\",\n          \"nullable\": false,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"image\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"image\",\n          \"nullable\": true,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"name\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"name\",\n          \"nullable\": true,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n      },\n      \"comment\": undefined,\n      \"foreignKeys\": {},\n      \"indexes\": [\n        {\n          \"columnNames\": [\n            \"email\",\n          ],\n          \"composite\": false,\n          \"keyName\": \"user_email_unique\",\n          \"primary\": false,\n          \"unique\": true,\n        },\n        {\n          \"columnNames\": [\n            \"id\",\n          ],\n          \"composite\": false,\n          \"expression\": undefined,\n          \"keyName\": \"primary\",\n          \"primary\": true,\n          \"type\": undefined,\n          \"unique\": true,\n        },\n      ],\n      \"name\": \"user\",\n      \"schema\": undefined,\n    },\n    {\n      \"checks\": [],\n      \"columns\": {\n        \"expires\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": 0,\n          \"mappedType\": \"datetime\",\n          \"name\": \"expires\",\n          \"nullable\": false,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"datetime\",\n          \"unsigned\": false,\n        },\n        \"id\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"id\",\n          \"nullable\": false,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"session_token\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"session_token\",\n          \"nullable\": false,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"user_id\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"user_id\",\n          \"nullable\": false,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n      },\n      \"comment\": undefined,\n      \"foreignKeys\": {\n        \"session_user_id_foreign\": {\n          \"columnNames\": [\n            \"user_id\",\n          ],\n          \"constraintName\": \"session_user_id_foreign\",\n          \"deleteRule\": \"cascade\",\n          \"localTableName\": \"session\",\n          \"referencedColumnNames\": [\n            \"id\",\n          ],\n          \"referencedTableName\": \"user\",\n          \"updateRule\": \"cascade\",\n        },\n      },\n      \"indexes\": [\n        {\n          \"columnNames\": [\n            \"user_id\",\n          ],\n          \"composite\": false,\n          \"keyName\": \"session_user_id_index\",\n          \"primary\": false,\n          \"unique\": false,\n        },\n        {\n          \"columnNames\": [\n            \"session_token\",\n          ],\n          \"composite\": false,\n          \"keyName\": \"session_session_token_unique\",\n          \"primary\": false,\n          \"unique\": true,\n        },\n        {\n          \"columnNames\": [\n            \"id\",\n          ],\n          \"composite\": false,\n          \"expression\": undefined,\n          \"keyName\": \"primary\",\n          \"primary\": true,\n          \"type\": undefined,\n          \"unique\": true,\n        },\n      ],\n      \"name\": \"session\",\n      \"schema\": undefined,\n    },\n    {\n      \"checks\": [],\n      \"columns\": {\n        \"access_token\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"access_token\",\n          \"nullable\": true,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"expires_at\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"integer\",\n          \"name\": \"expires_at\",\n          \"nullable\": true,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"integer\",\n          \"unsigned\": false,\n        },\n        \"id\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"id\",\n          \"nullable\": false,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"id_token\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"id_token\",\n          \"nullable\": true,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"provider\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"provider\",\n          \"nullable\": false,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"provider_account_id\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"provider_account_id\",\n          \"nullable\": false,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"refresh_token\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"refresh_token\",\n          \"nullable\": true,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"scope\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"scope\",\n          \"nullable\": true,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"session_state\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"session_state\",\n          \"nullable\": true,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"token_type\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"token_type\",\n          \"nullable\": true,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"type\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"type\",\n          \"nullable\": false,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"user_id\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"user_id\",\n          \"nullable\": false,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n      },\n      \"comment\": undefined,\n      \"foreignKeys\": {\n        \"account_user_id_foreign\": {\n          \"columnNames\": [\n            \"user_id\",\n          ],\n          \"constraintName\": \"account_user_id_foreign\",\n          \"deleteRule\": \"cascade\",\n          \"localTableName\": \"account\",\n          \"referencedColumnNames\": [\n            \"id\",\n          ],\n          \"referencedTableName\": \"user\",\n          \"updateRule\": \"cascade\",\n        },\n      },\n      \"indexes\": [\n        {\n          \"columnNames\": [\n            \"user_id\",\n          ],\n          \"composite\": false,\n          \"keyName\": \"account_user_id_index\",\n          \"primary\": false,\n          \"unique\": false,\n        },\n        {\n          \"columnNames\": [\n            \"provider\",\n            \"provider_account_id\",\n          ],\n          \"composite\": true,\n          \"expression\": undefined,\n          \"keyName\": \"account_provider_provider_account_id_unique\",\n          \"primary\": false,\n          \"type\": undefined,\n          \"unique\": true,\n        },\n        {\n          \"columnNames\": [\n            \"id\",\n          ],\n          \"composite\": false,\n          \"expression\": undefined,\n          \"keyName\": \"primary\",\n          \"primary\": true,\n          \"type\": undefined,\n          \"unique\": true,\n        },\n      ],\n      \"name\": \"account\",\n      \"schema\": undefined,\n    },\n    {\n      \"checks\": [],\n      \"columns\": {\n        \"expires\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": 0,\n          \"mappedType\": \"datetime\",\n          \"name\": \"expires\",\n          \"nullable\": false,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"datetime\",\n          \"unsigned\": false,\n        },\n        \"identifier\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"identifier\",\n          \"nullable\": false,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n        \"token\": {\n          \"autoincrement\": false,\n          \"comment\": undefined,\n          \"default\": undefined,\n          \"enumItems\": undefined,\n          \"extra\": undefined,\n          \"ignoreSchemaChanges\": undefined,\n          \"length\": undefined,\n          \"mappedType\": \"text\",\n          \"name\": \"token\",\n          \"nullable\": false,\n          \"precision\": undefined,\n          \"primary\": false,\n          \"scale\": undefined,\n          \"type\": \"text\",\n          \"unsigned\": false,\n        },\n      },\n      \"comment\": undefined,\n      \"foreignKeys\": {},\n      \"indexes\": [\n        {\n          \"columnNames\": [\n            \"token\",\n            \"identifier\",\n          ],\n          \"composite\": true,\n          \"expression\": undefined,\n          \"keyName\": \"verification_token_token_identifier_unique\",\n          \"primary\": false,\n          \"type\": undefined,\n          \"unique\": true,\n        },\n        {\n          \"columnNames\": [\n            \"token\",\n          ],\n          \"composite\": false,\n          \"expression\": undefined,\n          \"keyName\": \"primary\",\n          \"primary\": true,\n          \"type\": undefined,\n          \"unique\": true,\n        },\n      ],\n      \"name\": \"verification_token\",\n      \"schema\": undefined,\n    },\n  ],\n}\n`;\n"
  },
  {
    "path": "packages/adapter-mikro-orm/test/entities.test.ts",
    "content": "import type { SqliteDriver } from \"@mikro-orm/sqlite\"\nimport { MikroOrmAdapter, defaultEntities } from \"../src\"\nimport {\n  Cascade,\n  Collection,\n  Entity,\n  OneToMany,\n  PrimaryKey,\n  Property,\n  Unique,\n  MikroORM,\n  wrap,\n  Options,\n  types,\n} from \"@mikro-orm/core\"\nimport { runBasicTests } from \"utils/adapter\"\n\n@Entity()\nexport class User implements defaultEntities.User {\n  @PrimaryKey()\n  @Property({ type: types.string })\n  id: string = crypto.randomUUID()\n\n  @Property({ type: types.string, nullable: true })\n  name?: string\n\n  @Property({ type: types.string, nullable: true })\n  @Unique()\n  email: string = \"\"\n\n  @Property({ type: \"Date\", nullable: true })\n  emailVerified: Date | null = null\n\n  @Property({ type: types.string, nullable: true })\n  image?: string\n\n  @OneToMany({\n    entity: \"Session\",\n    mappedBy: (session: defaultEntities.Session) => session.user,\n    hidden: true,\n    orphanRemoval: true,\n    cascade: [Cascade.ALL],\n  })\n  sessions = new Collection<defaultEntities.Session>(this)\n\n  @OneToMany({\n    entity: \"Account\",\n    mappedBy: (account: defaultEntities.Account) => account.user,\n    hidden: true,\n    orphanRemoval: true,\n    cascade: [Cascade.ALL],\n  })\n  accounts = new Collection<defaultEntities.Account>(this)\n\n  @Property({ type: types.string, hidden: true })\n  role = \"ADMIN\"\n}\n\n@Entity()\nexport class VeryImportantEntity {\n  @PrimaryKey()\n  @Property({ type: types.string })\n  id: string = crypto.randomUUID()\n\n  @Property({ type: types.boolean })\n  important = true\n}\n\nlet _init: MikroORM\n\nconst entities = [\n  User,\n  defaultEntities.Account,\n  defaultEntities.Session,\n  defaultEntities.VerificationToken,\n  VeryImportantEntity,\n]\n\nconst config: Options<SqliteDriver> = {\n  dbName: \"./db.sqlite\",\n  type: \"sqlite\",\n  entities,\n  debug: process.env.DEBUG === \"true\" || process.env.DEBUG?.includes(\"db\"),\n}\n\nasync function getORM() {\n  if (_init) return _init\n\n  _init = await MikroORM.init(config)\n  return _init\n}\n\nrunBasicTests({\n  adapter: MikroOrmAdapter(config, { entities: { User } }),\n  db: {\n    async connect() {\n      const orm = await getORM()\n      await orm.getSchemaGenerator().dropSchema()\n      await orm.getSchemaGenerator().createSchema()\n    },\n    async disconnect() {\n      const orm = await getORM()\n      // its fine to tear down the connection if it has been already closed\n      await orm\n        .getSchemaGenerator()\n        .dropSchema()\n        .catch(() => null)\n      await orm.close().catch(() => null)\n    },\n    async verificationToken(identifier_token) {\n      const orm = await getORM()\n      const token = await orm.em\n        .fork()\n        .findOne(defaultEntities.VerificationToken, identifier_token)\n      if (!token) return null\n      return wrap(token).toObject()\n    },\n    async user(id) {\n      const orm = await getORM()\n      const user = await orm.em.fork().findOne(defaultEntities.User, { id })\n      if (!user) return null\n      return wrap(user).toObject()\n    },\n    async account(provider_providerAccountId) {\n      const orm = await getORM()\n      const account = await orm.em\n        .fork()\n        .findOne(defaultEntities.Account, { ...provider_providerAccountId })\n      if (!account) return null\n      return wrap(account).toObject()\n    },\n    async session(sessionToken) {\n      const orm = await getORM()\n      const session = await orm.em\n        .fork()\n        .findOne(defaultEntities.Session, { sessionToken })\n      if (!session) return null\n      return wrap(session).toObject()\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-mikro-orm/test/schema.test.ts",
    "content": "import { MikroORM, Options } from \"@mikro-orm/core\"\nimport { SqliteDriver } from \"@mikro-orm/sqlite\"\nimport { defaultEntities } from \"../src\"\nimport { expect, test } from \"vitest\"\n\nconst config: Options<SqliteDriver> = {\n  dbName: \"./db.sqlite\",\n  type: \"sqlite\",\n  entities: [\n    defaultEntities.User,\n    defaultEntities.Account,\n    defaultEntities.Session,\n    defaultEntities.VerificationToken,\n  ],\n}\n\ntest(\"run migrations\", async () => {\n  const orm = await MikroORM.init(config)\n  await orm.getSchemaGenerator().dropSchema()\n\n  const createSchemaSQL = await orm.getSchemaGenerator().getCreateSchemaSQL()\n  expect(createSchemaSQL).toMatchSnapshot(\"createSchemaSQL\")\n\n  const targetSchema = await orm.getSchemaGenerator().getTargetSchema()\n  expect(targetSchema).toMatchSnapshot(\"targetSchema\")\n\n  await orm.getSchemaGenerator().dropSchema()\n  await orm.close().catch(() => null)\n})\n"
  },
  {
    "path": "packages/adapter-mikro-orm/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-mikro-orm/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\", \"src/lib/entities.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/mikro-orm-adapter\",\n  entryFileName: \"../mikro-orm-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-mongodb/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://mongodb.com\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/mongodb.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>MongoDB Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/mongodb-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/mongodb-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/mongodb-adapter?color=green&label=@auth/mongodb-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/mongodb-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/mongodb-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/mongodb).\n"
  },
  {
    "path": "packages/adapter-mongodb/package.json",
    "content": "{\n  \"name\": \"@auth/mongodb-adapter\",\n  \"version\": \"3.11.0\",\n  \"description\": \"MongoDB adapter for Auth.js\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Balázs Orbán <info@balazsorban.com>\",\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"mongodb\",\n    \"adapter\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"test\": \"./test/test.sh\",\n    \"build\": \"tsc\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"mongodb\": \"^6\"\n  },\n  \"devDependencies\": {\n    \"mongodb\": \"^6.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-mongodb/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>Official <a href=\"https://www.mongodb.com\">MongoDB</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://www.mongodb.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/mongodb.svg\" width=\"30\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/mongodb-adapter mongodb\n * ```\n *\n * @module @auth/mongodb-adapter\n */\nimport { ObjectId } from \"mongodb\"\n\nimport type {\n  Adapter,\n  AdapterUser,\n  AdapterAccount,\n  AdapterSession,\n  VerificationToken,\n} from \"@auth/core/adapters\"\nimport type { MongoClient } from \"mongodb\"\n\n/**\n * This adapter uses https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management.\n * This feature is very new and requires runtime polyfills for `Symbol.asyncDispose` in order to work properly in all environments.\n * It is also required to set in the `tsconfig.json` file the compilation target to `es2022` or below and configure the `lib` option to include `esnext` or `esnext.disposable`.\n *\n * You can find more information about this feature and the polyfills in the link above.\n */\n// @ts-expect-error read only property is not assignable\nSymbol.asyncDispose ??= Symbol(\"Symbol.asyncDispose\")\n\n/** This is the interface of the MongoDB adapter options. */\nexport interface MongoDBAdapterOptions {\n  /**\n   * The name of the {@link https://www.mongodb.com/docs/manual/core/databases-and-collections/#collections MongoDB collections}.\n   */\n  collections?: {\n    Users?: string\n    Accounts?: string\n    Sessions?: string\n    VerificationTokens?: string\n  }\n  /**\n   * The name you want to give to the MongoDB database\n   */\n  databaseName?: string\n  /**\n   * Callback function for managing the closing of the MongoDB client.\n   * This could be useful when `client` is provided as a function returning MongoClient.\n   * It allows for more customized management of database connections,\n   * addressing persistence, container reuse, and connection closure issues.\n   */\n  onClose?: (client: MongoClient) => Promise<void>\n}\n\nexport const defaultCollections: Required<\n  Required<MongoDBAdapterOptions>[\"collections\"]\n> = {\n  Users: \"users\",\n  Accounts: \"accounts\",\n  Sessions: \"sessions\",\n  VerificationTokens: \"verification_tokens\",\n}\n\nexport const format = {\n  /** Takes a MongoDB object and returns a plain old JavaScript object */\n  from<T = Record<string, unknown>>(object: Record<string, any>): T {\n    const newObject: Record<string, unknown> = {}\n    for (const key in object) {\n      const value = object[key]\n      if (key === \"_id\") {\n        newObject.id = value.toHexString()\n      } else if (key === \"userId\") {\n        newObject[key] = value.toHexString()\n      } else {\n        newObject[key] = value\n      }\n    }\n    return newObject as T\n  },\n  /** Takes a plain old JavaScript object and turns it into a MongoDB object */\n  to<T = Record<string, unknown>>(object: Record<string, any>) {\n    const newObject: Record<string, unknown> = {\n      _id: _id(object.id),\n    }\n    for (const key in object) {\n      const value = object[key]\n      if (key === \"userId\") newObject[key] = _id(value)\n      else if (key === \"id\") continue\n      else newObject[key] = value\n    }\n    return newObject as T & { _id: ObjectId }\n  },\n}\n\n/** @internal */\nexport function _id(hex?: string) {\n  if (hex?.length !== 24) return new ObjectId()\n  return new ObjectId(hex)\n}\n\nexport function MongoDBAdapter(\n  /**\n   * The MongoDB client.\n   *\n   * The MongoDB team recommends providing a non-connected `MongoClient` instance to avoid unhandled promise rejections if the client fails to connect.\n   *\n   * Alternatively, you can also pass:\n   * - A promise that resolves to a connected `MongoClient` (not recommended).\n   * - A function, to handle more complex and custom connection strategies.\n   *\n   * Using a function combined with `options.onClose`, can be useful when you want a more advanced and customized connection strategy to address challenges related to persistence, container reuse, and connection closure.\n   */\n  client:\n    | MongoClient\n    | Promise<MongoClient>\n    | (() => MongoClient | Promise<MongoClient>),\n  options: MongoDBAdapterOptions = {}\n): Adapter {\n  const { collections } = options\n  const { from, to } = format\n\n  const getDb = async () => {\n    const _client: MongoClient = await (typeof client === \"function\"\n      ? client()\n      : client)\n    const _db = _client.db(options.databaseName)\n    const c = { ...defaultCollections, ...collections }\n    return {\n      U: _db.collection<AdapterUser>(c.Users),\n      A: _db.collection<AdapterAccount>(c.Accounts),\n      S: _db.collection<AdapterSession>(c.Sessions),\n      V: _db.collection<VerificationToken>(c?.VerificationTokens),\n      [Symbol.asyncDispose]: async () => {\n        await options.onClose?.(_client)\n      },\n    }\n  }\n\n  return {\n    async createUser(data) {\n      const user = to<AdapterUser>(data)\n      await using db = await getDb()\n      await db.U.insertOne(user)\n      return from<AdapterUser>(user)\n    },\n    async getUser(id) {\n      await using db = await getDb()\n      const user = await db.U.findOne({ _id: _id(id) })\n      if (!user) return null\n      return from<AdapterUser>(user)\n    },\n    async getUserByEmail(email) {\n      await using db = await getDb()\n      const user = await db.U.findOne({ email })\n      if (!user) return null\n      return from<AdapterUser>(user)\n    },\n    async getUserByAccount(provider_providerAccountId) {\n      await using db = await getDb()\n      const account = await db.A.findOne(provider_providerAccountId)\n      if (!account) return null\n      const user = await db.U.findOne({ _id: new ObjectId(account.userId) })\n      if (!user) return null\n      return from<AdapterUser>(user)\n    },\n    async updateUser(data) {\n      const { _id, ...user } = to<AdapterUser>(data)\n      await using db = await getDb()\n      const result = await db.U.findOneAndUpdate(\n        { _id },\n        { $set: user },\n        { returnDocument: \"after\" }\n      )\n\n      return from<AdapterUser>(result!)\n    },\n    async deleteUser(id) {\n      const userId = _id(id)\n      await using db = await getDb()\n      await Promise.all([\n        db.A.deleteMany({ userId: userId as any }),\n        db.S.deleteMany({ userId: userId as any }),\n        db.U.deleteOne({ _id: userId }),\n      ])\n    },\n    linkAccount: async (data) => {\n      const account = to<AdapterAccount>(data)\n      await using db = await getDb()\n      await db.A.insertOne(account)\n      return account\n    },\n    async unlinkAccount(provider_providerAccountId) {\n      await using db = await getDb()\n      const account = await db.A.findOneAndDelete(provider_providerAccountId)\n      return from<AdapterAccount>(account!)\n    },\n    async getSessionAndUser(sessionToken) {\n      await using db = await getDb()\n      const session = await db.S.findOne({ sessionToken })\n      if (!session) return null\n      const user = await db.U.findOne({ _id: new ObjectId(session.userId) })\n      if (!user) return null\n      return {\n        user: from<AdapterUser>(user),\n        session: from<AdapterSession>(session),\n      }\n    },\n    async createSession(data) {\n      const session = to<AdapterSession>(data)\n      await using db = await getDb()\n      await db.S.insertOne(session)\n      return from<AdapterSession>(session)\n    },\n    async updateSession(data) {\n      const { _id, ...session } = to<AdapterSession>(data)\n      await using db = await getDb()\n      const updatedSession = await db.S.findOneAndUpdate(\n        { sessionToken: session.sessionToken },\n        { $set: session },\n        { returnDocument: \"after\" }\n      )\n      return from<AdapterSession>(updatedSession!)\n    },\n    async deleteSession(sessionToken) {\n      await using db = await getDb()\n      const session = await db.S.findOneAndDelete({\n        sessionToken,\n      })\n      return from<AdapterSession>(session!)\n    },\n    async createVerificationToken(data) {\n      await using db = await getDb()\n      await db.V.insertOne(to(data))\n      return data\n    },\n    async useVerificationToken(identifier_token) {\n      await using db = await getDb()\n      const verificationToken = await db.V.findOneAndDelete(identifier_token)\n      if (!verificationToken) return null\n      const { _id, ...rest } = verificationToken\n      return rest\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-mongodb/test/custom.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { defaultCollections, format, MongoDBAdapter, _id } from \"../src\"\nimport { MongoClient } from \"mongodb\"\nconst name = \"custom-test\"\nconst client = new MongoClient(`mongodb://localhost:27017/${name}`)\n\nconst collections = { ...defaultCollections, Users: \"some_userz\" }\n\nrunBasicTests({\n  adapter: MongoDBAdapter(client, {\n    collections,\n  }),\n  db: {\n    async disconnect() {\n      await client.db().dropDatabase()\n      await client.close()\n    },\n    async user(id) {\n      const user = await client\n        .db()\n        .collection(collections.Users)\n        .findOne({ _id: _id(id) })\n\n      if (!user) return null\n      return format.from(user)\n    },\n    async account(provider_providerAccountId) {\n      const account = await client\n        .db()\n        .collection(collections.Accounts)\n        .findOne(provider_providerAccountId)\n      if (!account) return null\n      return format.from(account)\n    },\n    async session(sessionToken) {\n      const session = await client\n        .db()\n        .collection(collections.Sessions)\n        .findOne({ sessionToken })\n      if (!session) return null\n      return format.from(session)\n    },\n    async verificationToken(identifier_token) {\n      const token = await client\n        .db()\n        .collection(collections.VerificationTokens)\n        .findOne(identifier_token)\n      if (!token) return null\n      const { _id, ...rest } = token\n      return rest\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-mongodb/test/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { defaultCollections, format, MongoDBAdapter, _id } from \"../src\"\nimport { MongoClient } from \"mongodb\"\n\nconst name = \"test\"\nconst client = new MongoClient(`mongodb://localhost:27017/${name}`)\n\nrunBasicTests({\n  adapter: MongoDBAdapter(client),\n  db: {\n    async disconnect() {\n      await client.db().dropDatabase()\n      await client.close()\n    },\n    async user(id) {\n      const user = await client\n        .db()\n        .collection(defaultCollections.Users)\n        .findOne({ _id: _id(id) })\n\n      if (!user) return null\n      return format.from(user)\n    },\n    async account(provider_providerAccountId) {\n      const account = await client\n        .db()\n        .collection(defaultCollections.Accounts)\n        .findOne(provider_providerAccountId)\n      if (!account) return null\n      return format.from(account)\n    },\n    async session(sessionToken) {\n      const session = await client\n        .db()\n        .collection(defaultCollections.Sessions)\n        .findOne({ sessionToken })\n      if (!session) return null\n      return format.from(session)\n    },\n    async verificationToken(identifier_token) {\n      const token = await client\n        .db()\n        .collection(defaultCollections.VerificationTokens)\n        .findOne(identifier_token)\n      if (!token) return null\n      const { _id, ...rest } = token\n      return rest\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-mongodb/test/serverless.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { defaultCollections, format, MongoDBAdapter, _id } from \"../src\"\nimport { MongoClient } from \"mongodb\"\nimport { expect, test, vi } from \"vitest\"\n\nconst name = \"serverless-test\"\nconst client = new MongoClient(`mongodb://localhost:27017/${name}`)\n\nconst onClose = vi.fn(async (client: MongoClient) => {\n  await client.close()\n})\n\nlet mongoClientCount = 0\n\nrunBasicTests({\n  adapter: MongoDBAdapter(\n    async () => {\n      const client = await new MongoClient(\n        `mongodb://localhost:27017/${name}`\n      ).connect()\n      mongoClientCount++\n      return client\n    },\n    {\n      onClose,\n    }\n  ),\n  db: {\n    async disconnect() {\n      await client.db().dropDatabase()\n      await client.close()\n    },\n    async user(id) {\n      const user = await client\n        .db()\n        .collection(defaultCollections.Users)\n        .findOne({ _id: _id(id) })\n\n      if (!user) return null\n      return format.from(user)\n    },\n    async account(provider_providerAccountId) {\n      const account = await client\n        .db()\n        .collection(defaultCollections.Accounts)\n        .findOne(provider_providerAccountId)\n      if (!account) return null\n      return format.from(account)\n    },\n    async session(sessionToken) {\n      const session = await client\n        .db()\n        .collection(defaultCollections.Sessions)\n        .findOne({ sessionToken })\n      if (!session) return null\n      return format.from(session)\n    },\n    async verificationToken(identifier_token) {\n      const token = await client\n        .db()\n        .collection(defaultCollections.VerificationTokens)\n        .findOne(identifier_token)\n      if (!token) return null\n      const { _id, ...rest } = token\n      return rest\n    },\n  },\n})\n\ntest(\"all the connections are closed\", () => {\n  expect(mongoClientCount).toBeGreaterThan(0)\n  expect(onClose).toHaveBeenCalledTimes(mongoClientCount)\n})\n"
  },
  {
    "path": "packages/adapter-mongodb/test/test.sh",
    "content": "#!/usr/bin/env bash\n\nCONTAINER_NAME=authjs-mongodb-test\n\n# Start db\ndocker run -d --rm \\\n  -p 27017:27017 \\\n  --name ${CONTAINER_NAME} \\\n  mongo\n\necho \"Waiting 5s for db to start...\"\nsleep 5\n\n# Always stop container, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  docker stop ${CONTAINER_NAME}\nelse\n  docker stop ${CONTAINER_NAME} && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-mongodb/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-mongodb/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/mongodb-adapter\",\n  entryFileName: \"../mongodb-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-neo4j/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://neo4j.com\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/neo4j.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Neo4j Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/neo4j-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/neo4j-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/neo4j-adapter?color=green&label=@auth/neo4j-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/neo4j-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/neo4j-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/neo4j).\n"
  },
  {
    "path": "packages/adapter-neo4j/package.json",
    "content": "{\n  \"name\": \"@auth/neo4j-adapter\",\n  \"version\": \"2.11.0\",\n  \"description\": \"neo4j adapter for Auth.js\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Davey Brown\",\n  \"contributors\": [\n    \"Balázs Orbán <info@balazsorban.com>\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"neo4j\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"test\": \"./test/test.sh\",\n    \"build\": \"tsc\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"peerDependencies\": {\n    \"neo4j-driver\": \"^4.0.0 || ^5.7.0\"\n  },\n  \"devDependencies\": {\n    \"@types/uuid\": \"^8.3.3\",\n    \"neo4j-driver\": \"^5.7.0\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-neo4j/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>Official <a href=\"https://neo4j.com/docs/\">Neo4j</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://neo4j.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/neo4j.svg\" width=\"128\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/neo4j-adapter neo4j-driver\n * ```\n *\n * @module @auth/neo4j-adapter\n */\nimport { type Session, isInt, integer } from \"neo4j-driver\"\nimport { isDate, type Adapter } from \"@auth/core/adapters\"\n\n/**\n * This is the interface of the Neo4j adapter options. The Neo4j adapter takes a {@link https://neo4j.com/docs/bolt/current/driver-api/#driver-session Neo4j session} as its only argument.\n **/\nexport interface Neo4jOptions extends Session {}\n\nexport function Neo4jAdapter(session: Session): Adapter {\n  const { read, write } = client(session)\n\n  return {\n    async createUser(data) {\n      const user = { ...data, id: crypto.randomUUID() }\n      await write(`CREATE (u:User $data)`, user)\n      return user\n    },\n\n    async getUser(id) {\n      return await read(`MATCH (u:User { id: $id }) RETURN u{.*}`, {\n        id,\n      })\n    },\n\n    async getUserByEmail(email) {\n      return await read(`MATCH (u:User { email: $email }) RETURN u{.*}`, {\n        email,\n      })\n    },\n\n    async getUserByAccount(provider_providerAccountId) {\n      return await read(\n        `MATCH (u:User)-[:HAS_ACCOUNT]->(a:Account {\n           provider: $provider,\n           providerAccountId: $providerAccountId\n         })\n         RETURN u{.*}`,\n        provider_providerAccountId\n      )\n    },\n\n    async updateUser(data) {\n      return (\n        await write(\n          `MATCH (u:User { id: $data.id })\n           SET u += $data\n           RETURN u{.*}`,\n          data\n        )\n      ).u\n    },\n\n    async deleteUser(id) {\n      return await write(\n        `MATCH (u:User { id: $data.id })\n         WITH u, u{.*} AS properties\n         DETACH DELETE u\n         RETURN properties`,\n        { id }\n      )\n    },\n\n    async linkAccount(data) {\n      const { userId, ...a } = data\n      await write(\n        `MATCH (u:User { id: $data.userId })\n         MERGE (a:Account {\n           providerAccountId: $data.a.providerAccountId,\n           provider: $data.a.provider\n         }) \n         SET a += $data.a\n         MERGE (u)-[:HAS_ACCOUNT]->(a)`,\n        { userId, a }\n      )\n      return data\n    },\n\n    async unlinkAccount(provider_providerAccountId) {\n      return await write(\n        `MATCH (u:User)-[:HAS_ACCOUNT]->(a:Account {\n           providerAccountId: $data.providerAccountId,\n           provider: $data.provider\n         })\n         WITH u, a, properties(a) AS properties\n         DETACH DELETE a\n         RETURN properties { .*, userId: u.id }`,\n        provider_providerAccountId\n      )\n    },\n\n    async createSession(data) {\n      const { userId, ...s } = format.to(data)\n      await write(\n        `MATCH (u:User { id: $data.userId })\n         CREATE (s:Session)\n         SET s = $data.s\n         CREATE (u)-[:HAS_SESSION]->(s)`,\n        { userId, s }\n      )\n      return data\n    },\n\n    async getSessionAndUser(sessionToken) {\n      const result = await write(\n        `OPTIONAL MATCH (u:User)-[:HAS_SESSION]->(s:Session { sessionToken: $data.sessionToken })\n         WHERE s.expires <= datetime($data.now)\n         DETACH DELETE s\n         WITH count(s) AS c\n         MATCH (u:User)-[:HAS_SESSION]->(s:Session { sessionToken: $data.sessionToken })\n         RETURN s { .*, userId: u.id } AS session, u{.*} AS user`,\n        { sessionToken, now: new Date().toISOString() }\n      )\n\n      if (!result?.session || !result?.user) return null\n\n      return {\n        session: format.from<any>(result.session),\n        user: format.from<any>(result.user),\n      }\n    },\n\n    async updateSession(data) {\n      return await write(\n        `MATCH (u:User)-[:HAS_SESSION]->(s:Session { sessionToken: $data.sessionToken })\n         SET s += $data\n         RETURN s { .*, userId: u.id }`,\n        data\n      )\n    },\n\n    async deleteSession(sessionToken) {\n      return await write(\n        `MATCH (u:User)-[:HAS_SESSION]->(s:Session { sessionToken: $data.sessionToken })\n         WITH u, s, properties(s) AS properties\n         DETACH DELETE s\n         RETURN properties { .*, userId: u.id }`,\n        { sessionToken }\n      )\n    },\n\n    async createVerificationToken(data) {\n      await write(\n        `MERGE (v:VerificationToken {\n           identifier: $data.identifier,\n           token: $data.token\n         })\n         SET v = $data`,\n        data\n      )\n      return data\n    },\n\n    async useVerificationToken(data) {\n      const result = await write(\n        `MATCH (v:VerificationToken {\n           identifier: $data.identifier,\n           token: $data.token\n         })\n         WITH v, properties(v) as properties\n         DETACH DELETE v\n         RETURN properties`,\n        data\n      )\n      return format.from<any>(result?.properties)\n    },\n  }\n}\n\nexport const format = {\n  /** Takes a plain old JavaScript object and turns it into a Neo4j compatible object */\n  to(object: Record<string, any>) {\n    const newObject: Record<string, unknown> = {}\n    for (const key in object) {\n      const value = object[key]\n      if (value instanceof Date) newObject[key] = value.toISOString()\n      else newObject[key] = value\n    }\n    return newObject\n  },\n  /** Takes a Neo4j object and returns a plain old JavaScript object */\n  from<T = Record<string, unknown>>(object?: Record<string, any>): T | null {\n    const newObject: Record<string, unknown> = {}\n    if (!object) return null\n    for (const key in object) {\n      const value = object[key]\n      if (isDate(value)) {\n        newObject[key] = new Date(value)\n      } else if (isInt(value)) {\n        if (integer.inSafeRange(value)) newObject[key] = value.toNumber()\n        else newObject[key] = value.toString()\n      } else {\n        newObject[key] = value\n      }\n    }\n\n    return newObject as T\n  },\n}\n\nfunction client(session: Session) {\n  return {\n    /** Reads values from the database */\n    async read<T>(statement: string, values?: any): Promise<T | null> {\n      const result = await session.readTransaction((tx) =>\n        tx.run(statement, values)\n      )\n\n      return format.from<T>(result?.records[0]?.get(0)) ?? null\n    },\n    /**\n     * Reads/writes values from/to the database.\n     * Properties are available under `$data`\n     */\n    async write<T extends Record<string, any>>(\n      statement: string,\n      values: T\n    ): Promise<any> {\n      const result = await session.writeTransaction((tx) =>\n        tx.run(statement, { data: format.to(values) })\n      )\n\n      return format.from<T>(result?.records[0]?.toObject())\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-neo4j/test/index.test.ts",
    "content": "import * as neo4j from \"neo4j-driver\"\nimport { runBasicTests } from \"utils/adapter\"\nimport statements from \"./resources/statements\"\n\nimport { Neo4jAdapter, format } from \"../src\"\n\nconst driver = neo4j.driver(\n  \"bolt://localhost\",\n  neo4j.auth.basic(\"neo4j\", \"password\")\n)\n\nconst neo4jSession = driver.session()\n\nrunBasicTests({\n  adapter: Neo4jAdapter(neo4jSession),\n  db: {\n    async connect() {\n      for await (const statement of statements.split(\";\")) {\n        if (!statement.length) return\n        await neo4jSession.writeTransaction((tx) => tx.run(statement))\n      }\n    },\n    async disconnect() {\n      await neo4jSession.writeTransaction((tx) =>\n        tx.run(\n          `MATCH (n)\n           DETACH DELETE n\n           RETURN count(n)`\n        )\n      )\n      await neo4jSession.close()\n      await driver.close()\n    },\n    async user(id) {\n      const result = await neo4jSession.readTransaction((tx) =>\n        tx.run(`MATCH (u:User) RETURN u`, { id })\n      )\n      return format.from(result?.records[0]?.get(\"u\")?.properties)\n    },\n\n    async session(sessionToken: string) {\n      const result = await neo4jSession.readTransaction((tx) =>\n        tx.run(\n          `MATCH (u:User)-[:HAS_SESSION]->(s:Session)\n           RETURN s, u.id AS userId`,\n          { sessionToken }\n        )\n      )\n      const session = result?.records[0]?.get(\"s\")?.properties\n      const userId = result?.records[0]?.get(\"userId\")\n\n      if (!session || session.userId || !userId) return null\n\n      return { ...format.from(session), userId }\n    },\n\n    async account(provider_providerAccountId) {\n      const result = await neo4jSession.readTransaction((tx) =>\n        tx.run(\n          `MATCH (u:User)-[:HAS_ACCOUNT]->(a:Account)\n           RETURN a, u.id AS userId`,\n          provider_providerAccountId\n        )\n      )\n\n      const account = result?.records[0]?.get(\"a\")?.properties\n      const userId = result?.records[0]?.get(\"userId\")\n\n      if (!account || account.userId || !userId) return null\n\n      return { ...format.from(account), userId }\n    },\n\n    async verificationToken(identifier_token) {\n      const result = await neo4jSession.readTransaction((tx) =>\n        tx.run(\n          `MATCH (v:VerificationToken)\n           RETURN v`,\n          identifier_token\n        )\n      )\n\n      return format.from(result?.records[0]?.get(\"v\")?.properties)\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-neo4j/test/resources/statements.ts",
    "content": "// Constraints and indexes - relevant to Neo4j Community Edition.\nexport default `\nCREATE CONSTRAINT user_id_constraint IF NOT EXISTS\nON (u:User) ASSERT u.id IS UNIQUE;\n\nCREATE INDEX user_id_index IF NOT EXISTS\nFOR (u:User) ON (u.id);\n\nCREATE INDEX user_email_index IF NOT EXISTS\nFOR (u:User) ON (u.email);\n\nCREATE CONSTRAINT session_session_token_constraint IF NOT EXISTS\nON (s:Session) ASSERT s.sessionToken IS UNIQUE;\n\nCREATE INDEX session_session_token_index IF NOT EXISTS\nFOR (s:Session) ON (s.sessionToken);\n\nCREATE INDEX account_provider_index IF NOT EXISTS\nFOR (a:Account) ON (a.provider);\n\nCREATE INDEX account_provider_account_id_index IF NOT EXISTS\nFOR (a:Account) ON (a.providerAccountId);\n\nCREATE INDEX verification_token_identifier_index IF NOT EXISTS\nFOR (v:VerificationToken) ON (v.identifier);\n\nCREATE INDEX verification_token_token_index IF NOT EXISTS\nFOR (v:VerificationToken) ON (v.token);`\n"
  },
  {
    "path": "packages/adapter-neo4j/test/test.sh",
    "content": "#!/usr/bin/env bash\n\nNEO4J_USER=neo4j\nNEO4J_PASS=password\nCONTAINER_NAME=authjs-neo4j-test\n\n# Start db\ndocker run -d --rm \\\n  -e NEO4J_AUTH=${NEO4J_USER}/${NEO4J_PASS} \\\n  -e TEST_NEO4J_USER=${NEO4J_USER} \\\n  -e TEST_NEO4J_PASS=${NEO4J_PASS} \\\n  --name \"${CONTAINER_NAME}\" \\\n  -p7474:7474 -p7687:7687 \\\n  neo4j:4.2.0\n\n# For debug or testing it may be useful to use neo4j enterprise edition.\n# Use the lines below in the docker run statement.\n# -e NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \\\n# neo4j:4.2.0-enterprise\n\necho \"Waiting 5s for db to start...\" && sleep 5\n\n# Always stop container, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  docker stop \"${CONTAINER_NAME}\"\nelse\n  docker stop \"${CONTAINER_NAME}\" && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-neo4j/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-neo4j/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/neo4j-adapter\",\n  entryFileName: \"../neo4j-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-neon/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://neon.tech/\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://neon.tech/brand/neon-logo-dark-color.svg\" />\n  </a>\n  <h3 align=\"center\"><b>Neon Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/neon-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/neon-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/neon-adapter?color=green&label=@auth/neon-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/neon-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/neon-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/pg).\n"
  },
  {
    "path": "packages/adapter-neon/package.json",
    "content": "{\n  \"name\": \"@auth/neon-adapter\",\n  \"version\": \"1.11.0\",\n  \"description\": \"Neon Postgres adapter for next-auth.\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Jake Coppinger\",\n  \"contributors\": [\n    \"Thang Huu Vu <hi@thvu.dev>\"\n  ],\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"@auth\",\n    \"Auth.js\",\n    \"next.js\",\n    \"oauth\",\n    \"postgres\"\n  ],\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"files\": [\n    \"*.d.ts*\",\n    \"*.js\",\n    \"src\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"test\": \"./test/test.sh\",\n    \"build\": \"tsc\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"@neondatabase/serverless\": \"^0.10.4 || ^1.0.0\"\n  },\n  \"devDependencies\": {\n    \"@types/ws\": \"^8.5.13\",\n    \"ws\": \"^8.18.0\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-neon/schema.sql",
    "content": "\\set ON_ERROR_STOP true\n\nCREATE TABLE verification_token\n(\n  identifier TEXT NOT NULL,\n  expires TIMESTAMPTZ NOT NULL,\n  token TEXT NOT NULL,\n\n  PRIMARY KEY (identifier, token)\n);\n\nCREATE TABLE accounts\n(\n  id SERIAL,\n  \"userId\" INTEGER NOT NULL,\n  type VARCHAR(255) NOT NULL,\n  provider VARCHAR(255) NOT NULL,\n  \"providerAccountId\" VARCHAR(255) NOT NULL,\n  refresh_token TEXT,\n  access_token TEXT,\n  expires_at INTEGER,\n  id_token TEXT,\n  scope TEXT,\n  session_state TEXT,\n  token_type TEXT,\n\n  PRIMARY KEY (id)\n);\n\nCREATE TABLE sessions\n(\n  id SERIAL,\n  \"userId\" INTEGER NOT NULL,\n  expires TIMESTAMPTZ NOT NULL,\n  \"sessionToken\" VARCHAR(255) NOT NULL,\n\n  PRIMARY KEY (id)\n);\n\nCREATE TABLE users\n(\n  id SERIAL,\n  name VARCHAR(255),\n  email VARCHAR(255) UNIQUE,\n  \"emailVerified\" TIMESTAMPTZ,\n  image TEXT,\n\n  PRIMARY KEY (id)\n);\n"
  },
  {
    "path": "packages/adapter-neon/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>An official <a href=\"https://www.postgresql.org/\">PostgreSQL</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://www.postgresql.org/\">\n *   <img style={{display: \"block\"}} src=\"/img/adapters/neon.svg\" width=\"48\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install next-auth @auth/neon-adapter\n * ```\n *\n * @module @auth/neon-adapter\n */\n\nimport type {\n  Adapter,\n  AdapterUser,\n  VerificationToken,\n  AdapterSession,\n} from \"@auth/core/adapters\"\nimport type { Pool } from \"@neondatabase/serverless\"\n\nexport default function NeonAdapter(client: Pool): Adapter {\n  return {\n    async createVerificationToken(\n      verificationToken: VerificationToken\n    ): Promise<VerificationToken> {\n      const { identifier, expires, token } = verificationToken\n      const sql = `\n        INSERT INTO verification_token ( identifier, expires, token ) \n        VALUES ($1, $2, $3)\n        `\n      await client.query(sql, [identifier, expires, token])\n      return verificationToken\n    },\n    async useVerificationToken({\n      identifier,\n      token,\n    }: {\n      identifier: string\n      token: string\n    }): Promise<VerificationToken> {\n      const sql = `delete from verification_token\n      where identifier = $1 and token = $2\n      RETURNING identifier, expires, token `\n      const result = await client.query(sql, [identifier, token])\n      return result.rowCount !== 0 ? result.rows[0] : null\n    },\n\n    async createUser(user: Omit<AdapterUser, \"id\">) {\n      const { name, email, emailVerified, image } = user\n      const sql = `\n        INSERT INTO users (name, email, \"emailVerified\", image) \n        VALUES ($1, $2, $3, $4) \n        RETURNING id, name, email, \"emailVerified\", image`\n      const result = await client.query(sql, [\n        name,\n        email,\n        emailVerified,\n        image,\n      ])\n      return result.rows[0]\n    },\n    async getUser(id) {\n      const sql = `select * from users where id = $1`\n      try {\n        const result = await client.query(sql, [id])\n        return result.rowCount === 0 ? null : result.rows[0]\n      } catch {\n        return null\n      }\n    },\n    async getUserByEmail(email) {\n      const sql = `select * from users where email = $1`\n      const result = await client.query(sql, [email])\n      return result.rowCount !== 0 ? result.rows[0] : null\n    },\n    async getUserByAccount({\n      providerAccountId,\n      provider,\n    }): Promise<AdapterUser | null> {\n      const sql = `\n          select u.* from users u join accounts a on u.id = a.\"userId\"\n          where \n          a.provider = $1 \n          and \n          a.\"providerAccountId\" = $2`\n\n      const result = await client.query(sql, [provider, providerAccountId])\n      return result.rowCount !== 0 ? result.rows[0] : null\n    },\n    async updateUser(user: Partial<AdapterUser>): Promise<AdapterUser> {\n      const fetchSql = `select * from users where id = $1`\n      const query1 = await client.query(fetchSql, [user.id])\n      const oldUser = query1.rows[0]\n\n      const newUser = {\n        ...oldUser,\n        ...user,\n      }\n\n      const { id, name, email, emailVerified, image } = newUser\n      const updateSql = `\n        UPDATE users set\n        name = $2, email = $3, \"emailVerified\" = $4, image = $5\n        where id = $1\n        RETURNING name, id, email, \"emailVerified\", image\n      `\n      const query2 = await client.query(updateSql, [\n        id,\n        name,\n        email,\n        emailVerified,\n        image,\n      ])\n      return query2.rows[0]\n    },\n    async linkAccount(account) {\n      const sql = `\n      insert into accounts \n      (\n        \"userId\", \n        provider, \n        type, \n        \"providerAccountId\", \n        access_token,\n        expires_at,\n        refresh_token,\n        id_token,\n        scope,\n        session_state,\n        token_type\n      )\n      values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)\n      returning\n        id,\n        \"userId\", \n        provider, \n        type, \n        \"providerAccountId\", \n        access_token,\n        expires_at,\n        refresh_token,\n        id_token,\n        scope,\n        session_state,\n        token_type\n      `\n\n      const params = [\n        account.userId,\n        account.provider,\n        account.type,\n        account.providerAccountId,\n        account.access_token,\n        account.expires_at,\n        account.refresh_token,\n        account.id_token,\n        account.scope,\n        account.session_state,\n        account.token_type,\n      ]\n\n      const result = await client.query(sql, params)\n      return result.rows[0]\n    },\n    async createSession({ sessionToken, userId, expires }) {\n      if (userId === undefined) {\n        throw Error(`userId is undef in createSession`)\n      }\n      const sql = `insert into sessions (\"userId\", expires, \"sessionToken\")\n      values ($1, $2, $3)\n      RETURNING id, \"sessionToken\", \"userId\", expires`\n\n      const result = await client.query(sql, [userId, expires, sessionToken])\n      return result.rows[0]\n    },\n\n    async getSessionAndUser(sessionToken: string | undefined): Promise<{\n      session: AdapterSession\n      user: AdapterUser\n    } | null> {\n      if (sessionToken === undefined) {\n        return null\n      }\n      const result1 = await client.query(\n        `select * from sessions where \"sessionToken\" = $1`,\n        [sessionToken]\n      )\n      if (result1.rowCount === 0) {\n        return null\n      }\n      const session: AdapterSession = result1.rows[0]\n\n      const result2 = await client.query(\"select * from users where id = $1\", [\n        session.userId,\n      ])\n      if (result2.rowCount === 0) {\n        return null\n      }\n      const user = result2.rows[0]\n      return {\n        session,\n        user,\n      }\n    },\n    async updateSession(\n      session: Partial<AdapterSession> & Pick<AdapterSession, \"sessionToken\">\n    ): Promise<AdapterSession | null | undefined> {\n      const { sessionToken } = session\n      const result1 = await client.query(\n        `select * from sessions where \"sessionToken\" = $1`,\n        [sessionToken]\n      )\n      if (result1.rowCount === 0) {\n        return null\n      }\n      const originalSession: AdapterSession = result1.rows[0]\n\n      const newSession: AdapterSession = {\n        ...originalSession,\n        ...session,\n      }\n      const sql = `\n        UPDATE sessions set\n        expires = $2\n        where \"sessionToken\" = $1\n        `\n      const result = await client.query(sql, [\n        newSession.sessionToken,\n        newSession.expires,\n      ])\n      return result.rows[0]\n    },\n    async deleteSession(sessionToken) {\n      const sql = `delete from sessions where \"sessionToken\" = $1`\n      await client.query(sql, [sessionToken])\n    },\n    async unlinkAccount(partialAccount) {\n      const { provider, providerAccountId } = partialAccount\n      const sql = `delete from accounts where \"providerAccountId\" = $1 and provider = $2`\n      await client.query(sql, [providerAccountId, provider])\n    },\n    async deleteUser(userId: string) {\n      await client.query(`delete from users where id = $1`, [userId])\n      await client.query(`delete from sessions where \"userId\" = $1`, [userId])\n      await client.query(`delete from accounts where \"userId\" = $1`, [userId])\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-neon/test/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport NeonAdapter from \"../src\"\nimport { neonConfig, Pool } from \"@neondatabase/serverless\"\n\n// Using websockets\nimport ws from \"ws\"\nneonConfig.webSocketConstructor = ws\n\n// Some defaults to make Neon work with Postgres in Docker\n// Not required to do it in production\n// Set the WebSocket proxy to work with the local instance\nneonConfig.wsProxy = (host) => `${host}:5433/v1`\n// Disable all authentication and encryption\nneonConfig.pipelineTLS = false\nneonConfig.pipelineConnect = false\nneonConfig.useSecureWebSocket = false\n\nconst POOL_SIZE = 20\n\nconst client = new Pool({\n  host: \"127.0.0.1\",\n  database: \"postgres\",\n  user: \"postgres\",\n  password: \"postgres\",\n  port: 5432,\n  max: POOL_SIZE,\n})\n\nrunBasicTests({\n  adapter: NeonAdapter(client),\n  db: {\n    disconnect: async () => {\n      await client.end()\n    },\n    user: async (id: string) => {\n      const sql = `select * from users where id = $1`\n      const result = await client.query(sql, [id])\n      return result.rowCount !== 0 ? result.rows[0] : null\n    },\n    account: async (account) => {\n      const sql = `\n          select * from accounts where \"providerAccountId\" = $1`\n\n      const result = await client.query(sql, [account.providerAccountId])\n      return result.rowCount !== 0 ? result.rows[0] : null\n    },\n    session: async (sessionToken) => {\n      const result1 = await client.query(\n        `select * from sessions where \"sessionToken\" = $1`,\n        [sessionToken]\n      )\n      return result1.rowCount !== 0 ? result1.rows[0] : null\n    },\n    async verificationToken(identifier_token) {\n      const { identifier, token } = identifier_token\n      const sql = `\n          select * from verification_token where identifier = $1 and token = $2`\n\n      const result = await client.query(sql, [identifier, token])\n      return result.rowCount !== 0 ? result.rows[0] : null\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-neon/test/test.sh",
    "content": "#!/usr/bin/env bash\n\n# Start PostgreSQL container\ndocker run -d --rm \\\n  --name postgres \\\n  -e POSTGRES_DB=postgres \\\n  -e POSTGRES_USER=postgres \\\n  -e POSTGRES_PASSWORD=postgres \\\n  -p 5432:5432 \\\n  -v \"$(pwd)\"/schema.sql:/docker-entrypoint-initdb.d/schema.sql \\\n  postgres:latest\n\n# Start pg_proxy container\ndocker run -d --rm \\\n  --name pg_proxy \\\n  -e APPEND_PORT=\"postgres:5432\" \\\n  -e ALLOW_ADDR_REGEX=\".*\" \\\n  -e LOG_TRAFFIC=\"true\" \\\n  -p 5433:80 \\\n  --link postgres:postgres \\\n  ghcr.io/neondatabase/wsproxy:latest\n\necho \"waiting 5s for db to start...\"\nsleep 5\n\n# Always stop containers, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  docker stop pg_proxy && docker stop postgres\nelse\n  docker stop pg_proxy && docker stop postgres && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-neon/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-neon/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/neon-adapter\",\n  entryFileName: \"../neon-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-pg/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://www.postgresql.org/\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/pg.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Postgres Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/pg-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/pg-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/pg-adapter?color=green&label=@auth/pg-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/pg-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/pg-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/pg).\n"
  },
  {
    "path": "packages/adapter-pg/package.json",
    "content": "{\n  \"name\": \"@auth/pg-adapter\",\n  \"version\": \"1.11.0\",\n  \"description\": \"Postgres adapter for next-auth.\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Jake Coppinger\",\n  \"contributors\": [\n    \"Thang Huu Vu <hi@thvu.dev>\"\n  ],\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"@auth\",\n    \"Auth.js\",\n    \"next.js\",\n    \"oauth\",\n    \"postgres\"\n  ],\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"files\": [\n    \"*.d.ts*\",\n    \"*.js\",\n    \"src\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"test\": \"./test/test.sh\",\n    \"build\": \"tsc\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"pg\": \"^8\"\n  },\n  \"devDependencies\": {\n    \"@types/pg\": \"^8.6.5\",\n    \"pg\": \"^8.7.1\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-pg/schema.sql",
    "content": "\\set ON_ERROR_STOP true\n\nCREATE TABLE verification_token\n(\n  identifier TEXT NOT NULL,\n  expires TIMESTAMPTZ NOT NULL,\n  token TEXT NOT NULL,\n\n  PRIMARY KEY (identifier, token)\n);\n\nCREATE TABLE accounts\n(\n  id SERIAL,\n  \"userId\" INTEGER NOT NULL,\n  type VARCHAR(255) NOT NULL,\n  provider VARCHAR(255) NOT NULL,\n  \"providerAccountId\" VARCHAR(255) NOT NULL,\n  refresh_token TEXT,\n  access_token TEXT,\n  expires_at BIGINT,\n  id_token TEXT,\n  scope TEXT,\n  session_state TEXT,\n  token_type TEXT,\n\n  PRIMARY KEY (id)\n);\n\nCREATE TABLE sessions\n(\n  id SERIAL,\n  \"userId\" INTEGER NOT NULL,\n  expires TIMESTAMPTZ NOT NULL,\n  \"sessionToken\" VARCHAR(255) NOT NULL,\n\n  PRIMARY KEY (id)\n);\n\nCREATE TABLE users\n(\n  id SERIAL,\n  name VARCHAR(255),\n  email VARCHAR(255),\n  \"emailVerified\" TIMESTAMPTZ,\n  image TEXT,\n\n  PRIMARY KEY (id)\n);\n"
  },
  {
    "path": "packages/adapter-pg/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>An official <a href=\"https://www.postgresql.org/\">PostgreSQL</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://www.postgresql.org/\">\n *   <img style={{display: \"block\"}} src=\"/img/adapters/pg.svg\" width=\"48\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install next-auth @auth/pg-adapter pg\n * ```\n *\n * @module @auth/pg-adapter\n */\n\nimport type {\n  Adapter,\n  AdapterUser,\n  VerificationToken,\n  AdapterSession,\n} from \"@auth/core/adapters\"\nimport type { Pool } from \"pg\"\n\nexport function mapExpiresAt(account: any): any {\n  const expires_at: number = parseInt(account.expires_at)\n  return {\n    ...account,\n    expires_at,\n  }\n}\n\nexport default function PostgresAdapter(client: Pool): Adapter {\n  return {\n    async createVerificationToken(\n      verificationToken: VerificationToken\n    ): Promise<VerificationToken> {\n      const { identifier, expires, token } = verificationToken\n      const sql = `\n        INSERT INTO verification_token ( identifier, expires, token ) \n        VALUES ($1, $2, $3)\n        `\n      await client.query(sql, [identifier, expires, token])\n      return verificationToken\n    },\n    async useVerificationToken({\n      identifier,\n      token,\n    }: {\n      identifier: string\n      token: string\n    }): Promise<VerificationToken> {\n      const sql = `delete from verification_token\n      where identifier = $1 and token = $2\n      RETURNING identifier, expires, token `\n      const result = await client.query(sql, [identifier, token])\n      return result.rowCount !== 0 ? result.rows[0] : null\n    },\n\n    async createUser(user: Omit<AdapterUser, \"id\">) {\n      const { name, email, emailVerified, image } = user\n      const sql = `\n        INSERT INTO users (name, email, \"emailVerified\", image) \n        VALUES ($1, $2, $3, $4) \n        RETURNING id, name, email, \"emailVerified\", image`\n      const result = await client.query(sql, [\n        name,\n        email,\n        emailVerified,\n        image,\n      ])\n      return result.rows[0]\n    },\n    async getUser(id) {\n      const sql = `select * from users where id = $1`\n      try {\n        const result = await client.query(sql, [id])\n        return result.rowCount === 0 ? null : result.rows[0]\n      } catch {\n        return null\n      }\n    },\n    async getUserByEmail(email) {\n      const sql = `select * from users where email = $1`\n      const result = await client.query(sql, [email])\n      return result.rowCount !== 0 ? result.rows[0] : null\n    },\n    async getUserByAccount({\n      providerAccountId,\n      provider,\n    }): Promise<AdapterUser | null> {\n      const sql = `\n          select u.* from users u join accounts a on u.id = a.\"userId\"\n          where \n          a.provider = $1 \n          and \n          a.\"providerAccountId\" = $2`\n\n      const result = await client.query(sql, [provider, providerAccountId])\n      return result.rowCount !== 0 ? result.rows[0] : null\n    },\n    async updateUser(user: Partial<AdapterUser>): Promise<AdapterUser> {\n      const fetchSql = `select * from users where id = $1`\n      const query1 = await client.query(fetchSql, [user.id])\n      const oldUser = query1.rows[0]\n\n      const newUser = {\n        ...oldUser,\n        ...user,\n      }\n\n      const { id, name, email, emailVerified, image } = newUser\n      const updateSql = `\n        UPDATE users set\n        name = $2, email = $3, \"emailVerified\" = $4, image = $5\n        where id = $1\n        RETURNING name, id, email, \"emailVerified\", image\n      `\n      const query2 = await client.query(updateSql, [\n        id,\n        name,\n        email,\n        emailVerified,\n        image,\n      ])\n      return query2.rows[0]\n    },\n    async linkAccount(account) {\n      const sql = `\n      insert into accounts \n      (\n        \"userId\", \n        provider, \n        type, \n        \"providerAccountId\", \n        access_token,\n        expires_at,\n        refresh_token,\n        id_token,\n        scope,\n        session_state,\n        token_type\n      )\n      values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)\n      returning\n        id,\n        \"userId\", \n        provider, \n        type, \n        \"providerAccountId\", \n        access_token,\n        expires_at,\n        refresh_token,\n        id_token,\n        scope,\n        session_state,\n        token_type\n      `\n\n      const params = [\n        account.userId,\n        account.provider,\n        account.type,\n        account.providerAccountId,\n        account.access_token,\n        account.expires_at,\n        account.refresh_token,\n        account.id_token,\n        account.scope,\n        account.session_state,\n        account.token_type,\n      ]\n\n      const result = await client.query(sql, params)\n      return mapExpiresAt(result.rows[0])\n    },\n    async createSession({ sessionToken, userId, expires }) {\n      if (userId === undefined) {\n        throw Error(`userId is undef in createSession`)\n      }\n      const sql = `insert into sessions (\"userId\", expires, \"sessionToken\")\n      values ($1, $2, $3)\n      RETURNING id, \"sessionToken\", \"userId\", expires`\n\n      const result = await client.query(sql, [userId, expires, sessionToken])\n      return result.rows[0]\n    },\n\n    async getSessionAndUser(sessionToken: string | undefined): Promise<{\n      session: AdapterSession\n      user: AdapterUser\n    } | null> {\n      if (sessionToken === undefined) {\n        return null\n      }\n      const result1 = await client.query(\n        `select * from sessions where \"sessionToken\" = $1`,\n        [sessionToken]\n      )\n      if (result1.rowCount === 0) {\n        return null\n      }\n      const session: AdapterSession = result1.rows[0]\n\n      const result2 = await client.query(\"select * from users where id = $1\", [\n        session.userId,\n      ])\n      if (result2.rowCount === 0) {\n        return null\n      }\n      const user = result2.rows[0]\n      return {\n        session,\n        user,\n      }\n    },\n    async updateSession(\n      session: Partial<AdapterSession> & Pick<AdapterSession, \"sessionToken\">\n    ): Promise<AdapterSession | null | undefined> {\n      const { sessionToken } = session\n      const result1 = await client.query(\n        `select * from sessions where \"sessionToken\" = $1`,\n        [sessionToken]\n      )\n      if (result1.rowCount === 0) {\n        return null\n      }\n      const originalSession: AdapterSession = result1.rows[0]\n\n      const newSession: AdapterSession = {\n        ...originalSession,\n        ...session,\n      }\n      const sql = `\n        UPDATE sessions set\n        expires = $2\n        where \"sessionToken\" = $1\n        `\n      const result = await client.query(sql, [\n        newSession.sessionToken,\n        newSession.expires,\n      ])\n      return result.rows[0]\n    },\n    async deleteSession(sessionToken) {\n      const sql = `delete from sessions where \"sessionToken\" = $1`\n      await client.query(sql, [sessionToken])\n    },\n    async unlinkAccount(partialAccount) {\n      const { provider, providerAccountId } = partialAccount\n      const sql = `delete from accounts where \"providerAccountId\" = $1 and provider = $2`\n      await client.query(sql, [providerAccountId, provider])\n    },\n    async deleteUser(userId: string) {\n      await client.query(`delete from users where id = $1`, [userId])\n      await client.query(`delete from sessions where \"userId\" = $1`, [userId])\n      await client.query(`delete from accounts where \"userId\" = $1`, [userId])\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-pg/test/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport PostgresAdapter, { mapExpiresAt } from \"../src\"\nimport { Pool } from \"pg\"\n\nconst POOL_SIZE = 20\n\nconst client = new Pool({\n  host: \"127.0.0.1\",\n  database: \"adapter-postgres-test\",\n  user: \"pg\",\n  password: \"pg\",\n  port: 5432,\n  max: POOL_SIZE,\n})\n\nrunBasicTests({\n  adapter: PostgresAdapter(client),\n  db: {\n    disconnect: async () => {\n      await client.end()\n    },\n    user: async (id: string) => {\n      const sql = `select * from users where id = $1`\n      const result = await client.query(sql, [id])\n      return result.rowCount !== 0 ? result.rows[0] : null\n    },\n    account: async (account) => {\n      const sql = `\n          select * from accounts where \"providerAccountId\" = $1`\n\n      const result = await client.query(sql, [account.providerAccountId])\n      return result.rowCount !== 0 ? mapExpiresAt(result.rows[0]) : null\n    },\n    session: async (sessionToken) => {\n      const result1 = await client.query(\n        `select * from sessions where \"sessionToken\" = $1`,\n        [sessionToken]\n      )\n      return result1.rowCount !== 0 ? result1.rows[0] : null\n    },\n    async verificationToken(identifier_token) {\n      const { identifier, token } = identifier_token\n      const sql = `\n          select * from verification_token where identifier = $1 and token = $2`\n\n      const result = await client.query(sql, [identifier, token])\n      return result.rowCount !== 0 ? result.rows[0] : null\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-pg/test/test.sh",
    "content": "#!/usr/bin/env bash\n\nCONTAINER_NAME=authjs-pg-test\n\ndocker run -d --rm \\\n  --name ${CONTAINER_NAME} \\\n  -e POSTGRES_DB=adapter-postgres-test \\\n  -e POSTGRES_USER=pg \\\n  -e POSTGRES_PASSWORD=pg \\\n  -p 5432:5432 \\\n  -v \"$(pwd)\"/schema.sql:/docker-entrypoint-initdb.d/schema.sql \\\n  postgres:latest\n\necho \"waiting 5s for db to start...\"\nsleep 5\n\n# Always stop container, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  docker stop ${CONTAINER_NAME}\nelse\n  docker stop ${CONTAINER_NAME} && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-pg/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-pg/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/pg-adapter\",\n  entryFileName: \"../pg-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-pouchdb/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://pouchdb.com\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/pouchdb.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>PouchDB Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/pouchdb-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/pouchdb-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/pouchdb-adapter?color=green&label=@auth/pouchdb-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/pouchdb-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/pouchdb-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/pouchdb).\n"
  },
  {
    "path": "packages/adapter-pouchdb/package.json",
    "content": "{\n  \"name\": \"@auth/pouchdb-adapter\",\n  \"version\": \"2.11.0\",\n  \"description\": \"PouchDB adapter for next-auth.\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"jpbourgeon <jeanphilippe.bourgeon@gmail.com> (https://github.com/jpbourgeon)\",\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"pouchdb\"\n  ],\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"pnpm clean && tsc\",\n    \"clean\": \"rm -rf index.*\",\n    \"test\": \"vitest run -c ../utils/vitest.config.ts\"\n  },\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"peerDependencies\": {\n    \"pouchdb\": \"^8.0.1\",\n    \"pouchdb-find\": \"^8.0.1\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"@types/pouchdb\": \"^6.4.0\",\n    \"pouchdb\": \"^8.0.1\",\n    \"pouchdb-adapter-memory\": \"^8.0.1\",\n    \"pouchdb-find\": \"^8.0.1\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-pouchdb/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>Official <a href=\"https://pouchdb.com/api.html\">PouchDB</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://pouchdb.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/pouchdb.svg\" width=\"38\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install pouchdb pouchdb-find @auth/pouchdb-adapter\n * ```\n *\n * @module @auth/pouchdb-adapter\n */\n\nimport type {\n  Adapter,\n  AdapterAccount,\n  AdapterSession,\n  AdapterUser,\n  VerificationToken,\n} from \"@auth/core/adapters\"\n\ntype PrefixConfig = Record<\n  \"user\" | \"account\" | \"session\" | \"verificationToken\",\n  string\n>\ntype IndexConfig = Record<\n  | \"userByEmail\"\n  | \"accountByProviderId\"\n  | \"sessionByToken\"\n  | \"verificationTokenByToken\",\n  string\n>\n\n/**\n * Configure the adapter\n */\nexport interface PouchDBAdapterOptions {\n  /**\n   * Your PouchDB instance, with the `pouchdb-find` plugin installed.\n   * @example\n   * ```javascript\n   * import PouchDB from \"pouchdb\"\n   *\n   * PouchDB\n   *   .plugin(require(\"pouchdb-adapter-leveldb\")) // Or any other adapter\n   *   .plugin(require(\"pouchdb-find\")) // Don't forget the `pouchdb-find` plugin\n   *\n   * const pouchdb = new PouchDB(\"auth_db\", { adapter: \"leveldb\" })\n   */\n  pouchdb: PouchDB.Database\n  /**\n   * Override the default prefix names.\n   *\n   * @default\n   * ```js\n   * {\n   *   user: \"USER\",\n   *   account: \"ACCOUNT\",\n   *   session: \"SESSION\",\n   *   verificationToken: \"VERIFICATION-TOKEN\"\n   * }\n   * ```\n   */\n  prefixes?: PrefixConfig\n  /**\n   * Override the default index names.\n   *\n   * @default\n   * ```js\n   * {\n   *   userByEmail: \"nextAuthUserByEmail\",\n   *   accountByProviderId: \"nextAuthAccountByProviderId\",\n   *   sessionByToken: \"nextAuthSessionByToken\",\n   *   verificationTokenByToken: \"nextAuthVerificationRequestByToken\"\n   * }\n   * ```\n   */\n  indexes?: IndexConfig\n}\n\nexport function PouchDBAdapter(options: PouchDBAdapterOptions): Adapter {\n  const { pouchdb } = options\n  const {\n    userByEmail = \"nextAuthUserByEmail\",\n    accountByProviderId = \"nextAuthAccountByProviderId\",\n    sessionByToken = \"nextAuthSessionByToken\",\n    verificationTokenByToken = \"nextAuthVerificationRequestByToken\",\n  } = options?.indexes ?? {}\n  const {\n    user: userPrefix = \"USER\",\n    account: accountPrefix = \"ACCOUNT\",\n    session: sessionPrefix = \"SESSION\",\n    verificationToken: verificationTokenPrefix = \"VERIFICATION-TOKEN\",\n  } = options?.prefixes ?? {}\n\n  return {\n    async createUser(user) {\n      const doc = { ...user, _id: [userPrefix, crypto.randomUUID()].join(\"_\") }\n      await pouchdb.put(doc)\n      return { ...user, id: doc._id }\n    },\n\n    async getUser(id) {\n      try {\n        const res = await pouchdb.get<AdapterUser>(id)\n        return toAdapterUser(res)\n      } catch {\n        return null\n      }\n    },\n\n    async getUserByEmail(email) {\n      const res = await (\n        pouchdb as unknown as PouchDB.Database<AdapterUser>\n      ).find({\n        use_index: userByEmail,\n        selector: { email: { $eq: email } },\n        limit: 1,\n      })\n      const userDoc = res.docs[0]\n      if (userDoc) {\n        return toAdapterUser(userDoc)\n      }\n      return null\n    },\n\n    async getUserByAccount({ provider, providerAccountId }) {\n      const res = await (\n        pouchdb as unknown as PouchDB.Database<AdapterAccount>\n      ).find({\n        use_index: accountByProviderId,\n        selector: {\n          provider: { $eq: provider },\n          providerAccountId: { $eq: providerAccountId },\n        },\n        limit: 1,\n      })\n      const account = res.docs[0]\n      if (account) {\n        const user = await (\n          pouchdb as unknown as PouchDB.Database<AdapterUser>\n        ).get(account.userId)\n        return toAdapterUser(user) ?? null\n      }\n      return null\n    },\n\n    async updateUser(user) {\n      const doc = await (\n        pouchdb as unknown as PouchDB.Database<AdapterUser>\n      ).get(user.id!)\n      const newUser = {\n        ...doc,\n        ...user,\n      }\n      await pouchdb.put(newUser)\n      return toAdapterUser(newUser)\n    },\n\n    /** @todo Implement */\n    async deleteUser() {},\n\n    async linkAccount(account) {\n      const doc = {\n        ...account,\n        _id: [accountPrefix, crypto.randomUUID()].join(\"_\"),\n      }\n      await (pouchdb as unknown as PouchDB.Database<AdapterAccount>).put(doc)\n\n      return { ...account, id: doc._id }\n    },\n\n    async unlinkAccount({ provider, providerAccountId }) {\n      const _account = await (\n        pouchdb as unknown as PouchDB.Database<AdapterAccount>\n      ).find({\n        use_index: accountByProviderId,\n        selector: {\n          provider: { $eq: provider },\n          providerAccountId: { $eq: providerAccountId },\n        },\n        limit: 1,\n      })\n      await pouchdb.put({\n        ..._account.docs[0],\n        _deleted: true,\n      })\n    },\n\n    async createSession(data) {\n      const doc = {\n        _id: [sessionPrefix, crypto.randomUUID()].join(\"_\"),\n        ...data,\n      }\n      await (pouchdb as unknown as PouchDB.Database<AdapterSession>).put(doc)\n      return { ...data, id: doc._id }\n    },\n\n    async getSessionAndUser(sessionToken) {\n      const session = (\n        await (\n          pouchdb as unknown as PouchDB.Database<\n            AdapterSession & { user: AdapterUser }\n          >\n        ).find({\n          use_index: sessionByToken,\n          selector: {\n            sessionToken: { $eq: sessionToken },\n          },\n          limit: 1,\n        })\n      ).docs[0]\n\n      if (session) {\n        const user = await (\n          pouchdb as unknown as PouchDB.Database<AdapterUser>\n        ).get(session.userId)\n        return {\n          session: toAdapterSession(session),\n          user: toAdapterUser(user),\n        }\n      }\n      return null\n    },\n\n    async updateSession(data) {\n      const res = await (\n        pouchdb as unknown as PouchDB.Database<AdapterSession>\n      ).find({\n        use_index: sessionByToken,\n        selector: {\n          sessionToken: { $eq: data.sessionToken },\n        },\n        limit: 1,\n      })\n      const previousSessionDoc = res.docs[0]\n      if (previousSessionDoc) {\n        const currentSessionDoc = {\n          ...previousSessionDoc,\n          ...data,\n        }\n        await pouchdb.put(currentSessionDoc)\n        return toAdapterSession(currentSessionDoc)\n      }\n      return null\n    },\n\n    async deleteSession(sessionToken) {\n      const res = await (\n        pouchdb as unknown as PouchDB.Database<AdapterSession>\n      ).find({\n        use_index: sessionByToken,\n        selector: {\n          sessionToken: { $eq: sessionToken },\n        },\n        limit: 1,\n      })\n      const sessionDoc = res.docs[0]\n      await pouchdb.put({\n        ...sessionDoc,\n        _deleted: true,\n      })\n    },\n\n    async createVerificationToken(data) {\n      await (pouchdb as unknown as PouchDB.Database<VerificationToken>).put({\n        _id: [verificationTokenPrefix, crypto.randomUUID()].join(\"_\"),\n        ...data,\n      })\n\n      return data\n    },\n\n    async useVerificationToken({ identifier, token }) {\n      const res = await (\n        pouchdb as unknown as PouchDB.Database<VerificationToken>\n      ).find({\n        use_index: verificationTokenByToken,\n        selector: {\n          identifier: { $eq: identifier },\n          token: { $eq: token },\n        },\n        limit: 1,\n      })\n      const verificationRequestDoc = res.docs[0]\n      if (verificationRequestDoc) {\n        await pouchdb.put({\n          ...verificationRequestDoc,\n          _deleted: true,\n        })\n        return toVerificationToken(verificationRequestDoc)\n      }\n      return null\n    },\n  }\n}\n\nexport async function createIndexes(\n  pouchdb: PouchDB.Database,\n  indexes?: IndexConfig\n) {\n  const {\n    userByEmail = \"nextAuthUserByEmail\",\n    accountByProviderId = \"nextAuthAccountByProviderId\",\n    sessionByToken = \"nextAuthSessionByToken\",\n    verificationTokenByToken = \"nextAuthVerificationRequestByToken\",\n  } = indexes ?? {}\n  await Promise.allSettled([\n    await pouchdb.createIndex({\n      index: {\n        name: userByEmail,\n        ddoc: userByEmail,\n        fields: [\"email\"],\n      },\n    }),\n    await pouchdb.createIndex({\n      index: {\n        name: accountByProviderId,\n        ddoc: accountByProviderId,\n        fields: [\"provider\", \"providerAccountId\"],\n      },\n    }),\n    await pouchdb.createIndex({\n      index: {\n        name: sessionByToken,\n        ddoc: sessionByToken,\n        fields: [\"sessionToken\"],\n      },\n    }),\n    await pouchdb.createIndex({\n      index: {\n        name: verificationTokenByToken,\n        ddoc: verificationTokenByToken,\n        fields: [\"identifier\", \"token\"],\n      },\n    }),\n  ])\n}\n\n/** @internal */\nfunction toAdapter<T>(\n  dbObject: T & PouchDB.Core.IdMeta & PouchDB.Core.GetMeta\n) {\n  const {\n    _id,\n    _rev,\n    _conflicts,\n    _attachments,\n    _revisions,\n    _revs_info,\n    ...rest\n  } = dbObject\n  return { ...rest }\n}\n\n/** @internal */\nexport function toAdapterUser(\n  user: AdapterUser & PouchDB.Core.IdMeta & PouchDB.Core.GetMeta\n) {\n  if (typeof user?.emailVerified === \"string\")\n    user.emailVerified = new Date(user.emailVerified)\n  return { ...toAdapter(user), id: user._id }\n}\n\n/** @internal */\nexport function toAdapterSession(\n  session: AdapterSession & PouchDB.Core.IdMeta & PouchDB.Core.GetMeta\n) {\n  if (typeof session?.expires === \"string\")\n    session.expires = new Date(session.expires)\n  return { ...toAdapter(session), id: session._id }\n}\n\n/** @internal */\nexport function toAdapterAccount(\n  account: AdapterAccount & PouchDB.Core.IdMeta & PouchDB.Core.GetMeta\n) {\n  return { ...toAdapter(account), id: account._id }\n}\n\n/** @internal */\nexport function toVerificationToken(\n  verificationToken: VerificationToken &\n    PouchDB.Core.IdMeta &\n    PouchDB.Core.GetMeta\n) {\n  if (typeof verificationToken?.expires === \"string\")\n    verificationToken.expires = new Date(verificationToken.expires)\n  return { ...toAdapter(verificationToken) }\n}\n"
  },
  {
    "path": "packages/adapter-pouchdb/test/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport {\n  createIndexes,\n  PouchDBAdapter,\n  toAdapterAccount,\n  toAdapterSession,\n  toAdapterUser,\n  toVerificationToken,\n} from \"../src\"\nimport PouchDB from \"pouchdb\"\nimport find from \"pouchdb-find\"\nimport memoryAdapter from \"pouchdb-adapter-memory\"\nimport {\n  AdapterAccount,\n  AdapterSession,\n  AdapterUser,\n  VerificationToken,\n} from \"@auth/core/adapters\"\n\n// pouchdb setup\nPouchDB.plugin(memoryAdapter).plugin(find)\nlet pouchdb: PouchDB.Database\nlet pouchdbIsDestroyed: boolean = false\nPouchDB.on(\"created\", function () {\n  pouchdbIsDestroyed = false\n})\nPouchDB.on(\"destroyed\", function () {\n  pouchdbIsDestroyed = true\n})\nconst disconnect = async () => {\n  if (!pouchdbIsDestroyed) await pouchdb.destroy()\n}\npouchdb = new PouchDB(crypto.randomUUID(), { adapter: \"memory\" })\n\n// Basic tests\nrunBasicTests({\n  adapter: PouchDBAdapter({ pouchdb }),\n  skipTests: [\"deleteUser\"],\n  db: {\n    async connect() {\n      await createIndexes(pouchdb)\n    },\n    disconnect,\n    user: async (id) => {\n      try {\n        const res = await pouchdb.get<AdapterUser>(id)\n\n        return toAdapterUser(res)\n      } catch {\n        return null\n      }\n    },\n    account: async ({ provider, providerAccountId }) => {\n      const res = await (\n        pouchdb as unknown as PouchDB.Database<AdapterAccount>\n      ).find({\n        use_index: \"nextAuthAccountByProviderId\",\n        selector: {\n          provider: { $eq: provider },\n          providerAccountId: { $eq: providerAccountId },\n        },\n        limit: 1,\n      })\n      const doc = res.docs[0]\n      return doc ? toAdapterAccount(doc) : null\n    },\n    session: async (sessionToken) => {\n      const res = await (\n        pouchdb as unknown as PouchDB.Database<AdapterSession>\n      ).find({\n        use_index: \"nextAuthSessionByToken\",\n        selector: {\n          sessionToken: { $eq: sessionToken },\n        },\n        limit: 1,\n      })\n      const doc = res.docs[0]\n      return doc ? toAdapterSession(doc) : null\n    },\n    async verificationToken({ identifier, token }) {\n      const res = await (\n        pouchdb as unknown as PouchDB.Database<VerificationToken>\n      ).find({\n        use_index: \"nextAuthVerificationRequestByToken\",\n        selector: {\n          identifier: { $eq: identifier },\n          token: { $eq: token },\n        },\n        limit: 1,\n      })\n      const verificationRequest = res.docs[0]\n      return toVerificationToken(verificationRequest)\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-pouchdb/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-pouchdb/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/pouchdb-adapter\",\n  entryFileName: \"../pouchdb-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-prisma/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://prisma.io\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/prisma.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Prisma Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/prisma-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/prisma-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/prisma-adapter?color=green&label=@auth/prisma-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/prisma-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/prisma-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/prisma).\n"
  },
  {
    "path": "packages/adapter-prisma/package.json",
    "content": "{\n  \"name\": \"@auth/prisma-adapter\",\n  \"version\": \"2.11.0\",\n  \"description\": \"Prisma adapter for Auth.js\",\n  \"homepage\": \"https://authjs.dev/reference/adapter/prisma\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"William Luke\",\n  \"contributors\": [\n    \"Balázs Orbán <info@balazsorban.com>\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\",\n      \"default\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"prisma\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"clean\": \"rm ./prisma/dev.db* || echo 'File deleted' && rm -rf index.js *.d.ts*\",\n    \"init:default\": \"prisma migrate dev --name init --skip-seed\",\n    \"init:custom\": \"prisma migrate dev --name init-custom --schema ./prisma/custom.prisma\",\n    \"test:default\": \"pnpm init:default && vitest run -c ../utils/vitest.config.ts\",\n    \"test:custom\": \"pnpm init:custom && CUSTOM_MODEL=1 vitest run -c ../utils/vitest.config.ts\",\n    \"test:mongodb\": \"./test/mongodb.test.sh\",\n    \"test\": \"pnpm test:default && pnpm test:custom\",\n    \"test:og\": \"pnpm test:default && pnpm test:custom && pnpm test:mongodb\",\n    \"build\": \"prisma generate && tsc\",\n    \"dev\": \"prisma generate && tsc -w\",\n    \"studio\": \"prisma studio\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"@prisma/client\": \">=2.26.0 || >=3 || >=4 || >=5 || >=6\"\n  },\n  \"devDependencies\": {\n    \"@prisma/client\": \"^6.0.0\",\n    \"@prisma/extension-accelerate\": \"1.1.0\",\n    \"mongodb\": \"^6.9.0\",\n    \"prisma\": \"^6.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-prisma/prisma/custom.prisma",
    "content": "datasource db {\n  provider = \"sqlite\"\n  url      = \"file:./dev.db\"\n}\n\ngenerator client {\n  provider = \"prisma-client-js\"\n  previewFeatures = [\"strictUndefinedChecks\"]\n}\n\nmodel User {\n  id            String    @id @default(cuid())\n  name          String?\n  email         String?   @unique\n  emailVerified DateTime?\n  image         String?\n  phone         String?\n  role          String?\n  accounts      Account[]\n  sessions      Session[]\n  Authenticator Authenticator[]\n}\n\nmodel Account {\n  userId            String\n  type              String\n  provider          String\n  providerAccountId String\n  refresh_token     String?\n  access_token      String?\n  expires_at        Int?\n  token_type        String?\n  scope             String?\n  id_token          String?\n  session_state     String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@id([provider, providerAccountId])\n}\n\nmodel Session {\n  sessionToken String   @unique\n  userId       String\n  expires      DateTime\n  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n\nmodel VerificationToken {\n  identifier String\n  token      String   @unique\n  expires    DateTime\n\n  @@id([identifier, token])\n}\n\nmodel Authenticator {\n  id                   String  @id @default(cuid())\n  credentialID         String  @unique\n  userId               String\n  providerAccountId    String\n  credentialPublicKey  String\n  counter              Int\n  credentialDeviceType String\n  credentialBackedUp   Boolean\n  transports           String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n"
  },
  {
    "path": "packages/adapter-prisma/prisma/mongodb.prisma",
    "content": "datasource db {\n  provider = \"mongodb\"\n  url      = \"mongodb://localhost:27017/test\"\n}\n\ngenerator client {\n  provider = \"prisma-client-js\"\n  previewFeatures = [\"strictUndefinedChecks\"]\n}\n\nmodel Account {\n  id                String  @id @default(auto()) @map(\"_id\") @db.ObjectId\n  userId            String  @db.ObjectId\n  type              String\n  provider          String\n  providerAccountId String\n  refresh_token     String? @db.String\n  access_token      String? @db.String\n  expires_at        Int?\n  token_type        String?\n  scope             String?\n  id_token          String? @db.String\n  session_state     String?\n  user              User    @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@unique([provider, providerAccountId])\n}\n\nmodel Session {\n  id           String   @id @default(auto()) @map(\"_id\") @db.ObjectId\n  sessionToken String   @unique\n  userId       String   @db.ObjectId\n  expires      DateTime\n  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n\nmodel User {\n  id            String          @id @default(auto()) @map(\"_id\") @db.ObjectId\n  name          String?\n  email         String?         @unique\n  emailVerified DateTime?\n  image         String?\n  accounts      Account[]\n  sessions      Session[]\n  Authenticator Authenticator[]\n}\n\nmodel VerificationToken {\n  id         String   @id @default(auto()) @map(\"_id\") @db.ObjectId\n  identifier String\n  token      String   @unique\n  expires    DateTime\n\n  @@unique([identifier, token])\n}\n\nmodel Authenticator {\n  id                   String  @id @default(auto()) @map(\"_id\") @db.ObjectId\n  credentialID         String  @unique\n  userId               String  @db.ObjectId\n  providerAccountId    String\n  credentialPublicKey  String\n  counter              Int\n  credentialDeviceType String\n  credentialBackedUp   Boolean\n  transports           String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n"
  },
  {
    "path": "packages/adapter-prisma/prisma/schema.prisma",
    "content": "datasource db {\n  provider = \"sqlite\"\n  url      = \"file:./dev.db\"\n}\n\ngenerator client {\n  provider = \"prisma-client-js\"\n  previewFeatures = [\"strictUndefinedChecks\"]\n}\n\nmodel User {\n  id            String    @id @default(cuid())\n  name          String?\n  email         String    @unique\n  emailVerified DateTime?\n  image         String?\n  accounts      Account[]\n  sessions      Session[]\n  Authenticator Authenticator[]\n}\n\nmodel Account {\n  userId            String\n  type              String\n  provider          String\n  providerAccountId String\n  refresh_token     String?\n  access_token      String?\n  expires_at        Int?\n  token_type        String?\n  scope             String?\n  id_token          String?\n  session_state     String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@id([provider, providerAccountId])\n}\n\nmodel Session {\n  sessionToken String   @unique\n  userId       String\n  expires      DateTime\n  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n\nmodel VerificationToken {\n  identifier String\n  token      String   @unique\n  expires    DateTime\n\n  @@id([identifier, token])\n}\n\nmodel Authenticator {\n  credentialID         String  @unique\n  userId               String\n  providerAccountId    String\n  credentialPublicKey  String\n  counter              Int\n  credentialDeviceType String\n  credentialBackedUp   Boolean\n  transports           String?\n\n  user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@id([userId, credentialID])\n}\n"
  },
  {
    "path": "packages/adapter-prisma/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  Official <a href=\"https://www.prisma.io/docs\">Prisma</a> adapter for Auth.js / NextAuth.js.\n *  <a href=\"https://www.prisma.io/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/prisma.svg\" width=\"38\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @prisma/client @auth/prisma-adapter\n * npm install prisma --save-dev\n * ```\n *\n * @module @auth/prisma-adapter\n */\nimport type { PrismaClient } from \"@prisma/client\"\nimport type {\n  Adapter,\n  AdapterAccount,\n  AdapterSession,\n  AdapterUser,\n} from \"@auth/core/adapters\"\n\nexport function PrismaAdapter(\n  prisma: PrismaClient | ReturnType<PrismaClient[\"$extends\"]>\n): Adapter {\n  const p = prisma as PrismaClient\n  return {\n    // We need to let Prisma generate the ID because our default UUID is incompatible with MongoDB\n    createUser: ({ id, ...data }) => p.user.create(stripUndefined(data)),\n    getUser: (id) => p.user.findUnique({ where: { id } }),\n    getUserByEmail: (email) => p.user.findUnique({ where: { email } }),\n    async getUserByAccount(provider_providerAccountId) {\n      const account = await p.account.findUnique({\n        where: { provider_providerAccountId },\n        include: { user: true },\n      })\n      return (account?.user as AdapterUser) ?? null\n    },\n    updateUser: ({ id, ...data }) =>\n      p.user.update({\n        where: { id },\n        ...stripUndefined(data),\n      }) as Promise<AdapterUser>,\n    deleteUser: (id) =>\n      p.user.delete({ where: { id } }) as Promise<AdapterUser>,\n    linkAccount: (data) =>\n      p.account.create({ data }) as unknown as AdapterAccount,\n    unlinkAccount: (provider_providerAccountId) =>\n      p.account.delete({\n        where: { provider_providerAccountId },\n      }) as unknown as AdapterAccount,\n    async getSessionAndUser(sessionToken) {\n      const userAndSession = await p.session.findUnique({\n        where: { sessionToken },\n        include: { user: true },\n      })\n      if (!userAndSession) return null\n      const { user, ...session } = userAndSession\n      return { user, session } as { user: AdapterUser; session: AdapterSession }\n    },\n    createSession: (data) => p.session.create(stripUndefined(data)),\n    updateSession: (data) =>\n      p.session.update({\n        where: { sessionToken: data.sessionToken },\n        ...stripUndefined(data),\n      }),\n    deleteSession: (sessionToken) =>\n      p.session.delete({ where: { sessionToken } }),\n    async createVerificationToken(data) {\n      const verificationToken = await p.verificationToken.create(\n        stripUndefined(data)\n      )\n      if (\"id\" in verificationToken && verificationToken.id)\n        delete verificationToken.id\n      return verificationToken\n    },\n    async useVerificationToken(identifier_token) {\n      try {\n        const verificationToken = await p.verificationToken.delete({\n          where: { identifier_token },\n        })\n        if (\"id\" in verificationToken && verificationToken.id)\n          delete verificationToken.id\n        return verificationToken\n      } catch (error: unknown) {\n        // If token already used/deleted, just return null\n        // https://www.prisma.io/docs/reference/api-reference/error-reference#p2025\n        if (\n          error &&\n          typeof error === \"object\" &&\n          \"code\" in error &&\n          error.code === \"P2025\"\n        )\n          return null\n        throw error\n      }\n    },\n    async getAccount(providerAccountId, provider) {\n      return p.account.findFirst({\n        where: { providerAccountId, provider },\n      }) as Promise<AdapterAccount | null>\n    },\n    async createAuthenticator(data) {\n      return p.authenticator.create(stripUndefined(data))\n    },\n    async getAuthenticator(credentialID) {\n      return p.authenticator.findUnique({\n        where: { credentialID },\n      })\n    },\n    async listAuthenticatorsByUserId(userId) {\n      return p.authenticator.findMany({\n        where: { userId },\n      })\n    },\n    async updateAuthenticatorCounter(credentialID, counter) {\n      return p.authenticator.update({\n        where: { credentialID },\n        data: { counter },\n      })\n    },\n  }\n}\n\n/** @see https://www.prisma.io/docs/orm/prisma-client/special-fields-and-types/null-and-undefined */\nfunction stripUndefined<T>(obj: T) {\n  const data = {} as T\n  for (const key in obj) if (obj[key] !== undefined) data[key] = obj[key]\n  return { data }\n}\n"
  },
  {
    "path": "packages/adapter-prisma/test/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { PrismaClient } from \"@prisma/client\"\nimport { PrismaAdapter } from \"../src\"\nimport { ObjectId } from \"mongodb\"\nimport { withAccelerate } from \"@prisma/extension-accelerate\"\n\nconst prisma = new PrismaClient().$extends(withAccelerate())\n\nrunBasicTests({\n  adapter: PrismaAdapter(prisma),\n  testWebAuthnMethods: true,\n  db: {\n    id() {\n      if (process.env.CONTAINER_NAME !== \"authjs-mongodb-test\") return\n      return new ObjectId().toHexString()\n    },\n    connect: async () => {\n      await Promise.all([\n        prisma.user.deleteMany({}),\n        prisma.account.deleteMany({}),\n        prisma.session.deleteMany({}),\n        prisma.verificationToken.deleteMany({}),\n        prisma.authenticator.deleteMany({}),\n      ])\n    },\n    disconnect: async () => {\n      await Promise.all([\n        prisma.user.deleteMany({}),\n        prisma.account.deleteMany({}),\n        prisma.session.deleteMany({}),\n        prisma.verificationToken.deleteMany({}),\n        prisma.authenticator.deleteMany({}),\n      ])\n      await prisma.$disconnect()\n    },\n    user: (id) => prisma.user.findUnique({ where: { id } }),\n    account: (provider_providerAccountId) =>\n      prisma.account.findUnique({ where: { provider_providerAccountId } }),\n    session: (sessionToken) =>\n      prisma.session.findUnique({ where: { sessionToken } }),\n    async verificationToken(identifier_token) {\n      const result = await prisma.verificationToken.findUnique({\n        where: { identifier_token },\n      })\n      if (!result) return null\n      // @ts-ignore // MongoDB needs an ID, but we don't\n      delete result.id\n      return result\n    },\n    authenticator: (credentialID) =>\n      prisma.authenticator.findUnique({ where: { credentialID } }),\n  },\n})\n"
  },
  {
    "path": "packages/adapter-prisma/test/mongodb.test.sh",
    "content": "#!/usr/bin/env bash\n\nCONTAINER_NAME=authjs-mongodb-test\n\n# Start db\ndocker run -d --rm \\\n  -p 27017:27017 \\\n  --name ${CONTAINER_NAME} \\\n  \"prismagraphql/mongo-single-replica:4.4.3-bionic\"\n\npnpm prisma generate --schema ./prisma/mongodb.prisma\n\nexport CONTAINER_NAME=authjs-mongodb-test\n# Always stop container, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  docker stop ${CONTAINER_NAME}\nelse\n  docker stop ${CONTAINER_NAME} && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-prisma/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-prisma/typedoc.config.js",
    "content": "export default {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/prisma-adapter\",\n  entryFileName: \"../prisma-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n};\n"
  },
  {
    "path": "packages/adapter-sequelize/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://sequelize.org\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/sequelize.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Sequelize Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/sequelize-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/sequelize-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/sequelize-adapter?color=green&label=@auth/sequelize-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/sequelize-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/sequelize-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/sequelize).\n"
  },
  {
    "path": "packages/adapter-sequelize/package.json",
    "content": "{\n  \"name\": \"@auth/sequelize-adapter\",\n  \"version\": \"2.11.0\",\n  \"description\": \"Sequelize adapter for Auth.js\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"github.com/luke-j\",\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"sequelize\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"test\": \"vitest -c ../utils/vitest.config.ts\",\n    \"build\": \"tsc\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"sequelize\": \"^6.6.5\"\n  },\n  \"devDependencies\": {\n    \"sequelize\": \"^6.6.5\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-sequelize/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>Official <a href=\"https://sequelize.org/docs/v6/getting-started/\">Sequilize</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://sequelize.org/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/sequelize.svg\" height=\"30\"/>\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install next-auth @auth/sequelize-adapter sequelize\n * ```\n *\n * @module @auth/sequelize-adapter\n */\nimport type {\n  Adapter,\n  AdapterUser,\n  AdapterAccount,\n  AdapterSession,\n  VerificationToken,\n} from \"@auth/core/adapters\"\nimport { Sequelize, Model, ModelCtor } from \"sequelize\"\nimport * as defaultModels from \"./models.js\"\n\nexport { defaultModels as models }\n\n// @see https://sequelize.org/master/manual/typescript.html\n//@ts-expect-error\ninterface AccountInstance\n  extends Model<AdapterAccount, Partial<AdapterAccount>>,\n    AdapterAccount {}\ninterface UserInstance\n  extends Model<AdapterUser, Partial<AdapterUser>>,\n    AdapterUser {}\ninterface SessionInstance\n  extends Model<AdapterSession, Partial<AdapterSession>>,\n    AdapterSession {}\ninterface VerificationTokenInstance\n  extends Model<VerificationToken, Partial<VerificationToken>>,\n    VerificationToken {}\n\n/** This is the interface of the Sequelize adapter options. */\nexport interface SequelizeAdapterOptions {\n  /**\n   * Whether to {@link https://sequelize.org/docs/v6/core-concepts/model-basics/#model-synchronization synchronize} the models or not.\n   */\n  synchronize?: boolean\n  /**\n   * The {@link https://sequelize.org/docs/v6/core-concepts/model-basics/ Sequelize Models} related to Auth.js that will be created in your database.\n   */\n  models?: Partial<{\n    User: ModelCtor<UserInstance>\n    Account: ModelCtor<AccountInstance>\n    Session: ModelCtor<SessionInstance>\n    VerificationToken: ModelCtor<VerificationTokenInstance>\n  }>\n}\n\nexport default function SequelizeAdapter(\n  client: Sequelize,\n  options?: SequelizeAdapterOptions\n): Adapter {\n  const { models, synchronize = true } = options ?? {}\n  const defaultModelOptions = { underscored: true, timestamps: false }\n  const { User, Account, Session, VerificationToken } = {\n    User:\n      models?.User ??\n      client.define<UserInstance>(\n        \"user\",\n        defaultModels.User,\n        defaultModelOptions\n      ),\n    Account:\n      models?.Account ??\n      client.define<AccountInstance>(\n        \"account\",\n        defaultModels.Account,\n        defaultModelOptions\n      ),\n    Session:\n      models?.Session ??\n      client.define<SessionInstance>(\n        \"session\",\n        defaultModels.Session,\n        defaultModelOptions\n      ),\n    VerificationToken:\n      models?.VerificationToken ??\n      client.define<VerificationTokenInstance>(\n        \"verificationToken\",\n        defaultModels.VerificationToken,\n        defaultModelOptions\n      ),\n  }\n  let _synced = false\n  const sync = async () => {\n    if (process.env.NODE_ENV !== \"production\" && synchronize && !_synced) {\n      const syncOptions =\n        typeof synchronize === \"object\" ? synchronize : undefined\n\n      await Promise.all([\n        User.sync(syncOptions),\n        Account.sync(syncOptions),\n        Session.sync(syncOptions),\n        VerificationToken.sync(syncOptions),\n      ])\n\n      _synced = true\n    }\n  }\n\n  Account.belongsTo(User, { onDelete: \"cascade\" })\n  Session.belongsTo(User, { onDelete: \"cascade\" })\n\n  return {\n    async createUser(user) {\n      await sync()\n\n      return await User.create(user)\n    },\n    async getUser(id) {\n      await sync()\n\n      const userInstance = await User.findByPk(id)\n\n      return userInstance?.get({ plain: true }) ?? null\n    },\n    async getUserByEmail(email) {\n      await sync()\n\n      const userInstance = await User.findOne({\n        where: { email },\n      })\n\n      return userInstance?.get({ plain: true }) ?? null\n    },\n    async getUserByAccount({ provider, providerAccountId }) {\n      await sync()\n\n      const accountInstance = await Account.findOne({\n        // @ts-expect-error\n        where: { provider, providerAccountId },\n      })\n\n      if (!accountInstance) {\n        return null\n      }\n\n      const userInstance = await User.findByPk(accountInstance.userId)\n\n      return userInstance?.get({ plain: true }) ?? null\n    },\n    async updateUser(user) {\n      await sync()\n\n      await User.update(user, { where: { id: user.id } })\n      const userInstance = await User.findByPk(user.id)\n\n      return userInstance!\n    },\n    async deleteUser(userId) {\n      await sync()\n\n      const userInstance = await User.findByPk(userId)\n\n      await User.destroy({ where: { id: userId } })\n\n      return userInstance\n    },\n    async linkAccount(account) {\n      await sync()\n\n      await Account.create(account)\n    },\n    async unlinkAccount({ provider, providerAccountId }) {\n      await sync()\n\n      await Account.destroy({\n        where: { provider, providerAccountId },\n      })\n    },\n    async createSession(session) {\n      await sync()\n\n      return await Session.create(session)\n    },\n    async getSessionAndUser(sessionToken) {\n      await sync()\n\n      const sessionInstance = await Session.findOne({\n        where: { sessionToken },\n      })\n\n      if (!sessionInstance) {\n        return null\n      }\n\n      const userInstance = await User.findByPk(sessionInstance.userId)\n\n      if (!userInstance) {\n        return null\n      }\n\n      return {\n        session: sessionInstance?.get({ plain: true }),\n        user: userInstance?.get({ plain: true }),\n      }\n    },\n    async updateSession({ sessionToken, expires }) {\n      await sync()\n\n      await Session.update(\n        { expires, sessionToken },\n        { where: { sessionToken } }\n      )\n\n      return await Session.findOne({ where: { sessionToken } })\n    },\n    async deleteSession(sessionToken) {\n      await sync()\n\n      const session = await Session.findOne({ where: { sessionToken } })\n      await Session.destroy({ where: { sessionToken } })\n      return session?.get({ plain: true })\n    },\n    async createVerificationToken(token) {\n      await sync()\n\n      return await VerificationToken.create(token)\n    },\n    async useVerificationToken({ identifier, token }) {\n      await sync()\n\n      const tokenInstance = await VerificationToken.findOne({\n        where: { identifier, token },\n      })\n\n      await VerificationToken.destroy({ where: { identifier } })\n\n      return tokenInstance?.get({ plain: true }) ?? null\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-sequelize/src/models.ts",
    "content": "import { DataTypes } from \"sequelize\"\n\nexport const Account = {\n  id: {\n    type: DataTypes.UUID,\n    defaultValue: DataTypes.UUIDV4,\n    primaryKey: true,\n  },\n  type: { type: DataTypes.STRING, allowNull: false },\n  provider: { type: DataTypes.STRING, allowNull: false },\n  providerAccountId: { type: DataTypes.STRING, allowNull: false },\n  refresh_token: { type: DataTypes.STRING },\n  access_token: { type: DataTypes.STRING },\n  expires_at: { type: DataTypes.INTEGER },\n  token_type: { type: DataTypes.STRING },\n  scope: { type: DataTypes.STRING },\n  id_token: { type: DataTypes.TEXT },\n  session_state: { type: DataTypes.STRING },\n  userId: { type: DataTypes.UUID },\n}\n\nexport const User = {\n  id: {\n    type: DataTypes.UUID,\n    defaultValue: DataTypes.UUIDV4,\n    primaryKey: true,\n  },\n  name: { type: DataTypes.STRING },\n  email: { type: DataTypes.STRING, unique: \"email\" },\n  emailVerified: { type: DataTypes.DATE },\n  image: { type: DataTypes.STRING },\n}\n\nexport const Session = {\n  id: {\n    type: DataTypes.UUID,\n    defaultValue: DataTypes.UUIDV4,\n    primaryKey: true,\n  },\n  expires: { type: DataTypes.DATE, allowNull: false },\n  sessionToken: {\n    type: DataTypes.STRING,\n    unique: \"sessionToken\",\n    allowNull: false,\n  },\n  userId: { type: DataTypes.UUID },\n}\n\nexport const VerificationToken = {\n  token: { type: DataTypes.STRING, primaryKey: true },\n  identifier: { type: DataTypes.STRING, allowNull: false },\n  expires: { type: DataTypes.DATE, allowNull: false },\n}\n"
  },
  {
    "path": "packages/adapter-sequelize/test/index.test.ts",
    "content": "import { beforeEach, describe, expect, test } from \"vitest\"\nimport { Sequelize, DataTypes } from \"sequelize\"\nimport { runBasicTests } from \"utils/adapter\"\nimport SequelizeAdapter, { models } from \"../src\"\n\nconst sequelize = new Sequelize({\n  logging: false,\n  dialect: \"sqlite\",\n  storage: \":memory:\",\n})\n\nrunBasicTests({\n  adapter: SequelizeAdapter(sequelize),\n  db: {\n    connect: async () => {\n      return await sequelize.sync({ force: true })\n    },\n    verificationToken: async (where) => {\n      const verificationToken =\n        await sequelize.models.verificationToken.findOne({ where })\n\n      return verificationToken?.get({ plain: true }) || null\n    },\n    user: async (id) => {\n      const user = await sequelize.models.user.findByPk(id)\n\n      return user?.get({ plain: true }) || null\n    },\n    account: async (where) => {\n      const account = await sequelize.models.account.findOne({ where })\n\n      return account?.get({ plain: true }) || null\n    },\n    session: async (sessionToken) => {\n      const session = await sequelize.models.session.findOne({\n        where: { sessionToken },\n      })\n\n      return session?.get({ plain: true }) || null\n    },\n  },\n})\n\ndescribe(\"Additional Sequelize tests\", () => {\n  describe(\"synchronize option\", () => {\n    const lowercase = (strs: string[]) =>\n      strs.map((s) => s.replace(/[^a-z]/gi, \"\").toLowerCase())\n\n    beforeEach(async () => {\n      await sequelize.getQueryInterface().dropAllTables()\n\n      const { getUser } = SequelizeAdapter(sequelize)\n\n      await getUser?.(\"1\")\n    })\n\n    test(\"Creates DB tables\", async () => {\n      const tables = await sequelize.getQueryInterface().showAllSchemas()\n\n      expect(tables).toEqual([\n        { name: \"users\" },\n        { name: \"accounts\" },\n        { name: \"sessions\" },\n        { name: \"verification_tokens\" },\n      ])\n    })\n\n    test(\"Correctly creates users table\", async () => {\n      const table = await sequelize.getQueryInterface().describeTable(\"users\")\n\n      expect(lowercase(Object.keys(table))).toEqual(\n        lowercase(Object.keys(models.User))\n      )\n    })\n\n    test(\"Correctly creates accounts table\", async () => {\n      const table = await sequelize\n        .getQueryInterface()\n        .describeTable(\"accounts\")\n\n      expect(lowercase(Object.keys(table))).toEqual(\n        lowercase(Object.keys(models.Account))\n      )\n    })\n\n    test(\"Correctly creates sessions table\", async () => {\n      const table = await sequelize\n        .getQueryInterface()\n        .describeTable(\"sessions\")\n\n      expect(lowercase(Object.keys(table))).toEqual(\n        lowercase(Object.keys(models.Session))\n      )\n    })\n\n    test(\"Correctly creates verification_tokens table\", async () => {\n      const table = await sequelize\n        .getQueryInterface()\n        .describeTable(\"verification_tokens\")\n\n      expect(lowercase(Object.keys(table))).toEqual(\n        lowercase(Object.keys(models.VerificationToken))\n      )\n    })\n  })\n\n  describe(\"overriding models\", () => {\n    beforeEach(async () => {\n      await sequelize.getQueryInterface().dropAllTables()\n\n      const { getUser } = SequelizeAdapter(sequelize, {\n        synchronize: true,\n        models: {\n          User: sequelize.define(\"users\", {\n            ...models.User,\n            someUserAttribute: { type: DataTypes.STRING },\n          }),\n          Account: sequelize.define(\"accounts\", {\n            ...models.Account,\n            someAccountAttribute: { type: DataTypes.STRING },\n          }),\n          Session: sequelize.define(\"sessions\", {\n            ...models.Session,\n            someSessionAttribute: { type: DataTypes.STRING },\n          }),\n          VerificationToken: sequelize.define(\"verification_tokens\", {\n            ...models.VerificationToken,\n            someVerificationTokenAttribute: { type: DataTypes.STRING },\n          }),\n        },\n      })\n\n      await getUser?.(\"1\")\n    })\n\n    test(\"Custom user model\", async () => {\n      const table = await sequelize.getQueryInterface().describeTable(\"users\")\n\n      expect(table.someUserAttribute).toBeDefined()\n    })\n\n    test(\"Custom account model\", async () => {\n      const table = await sequelize\n        .getQueryInterface()\n        .describeTable(\"accounts\")\n\n      expect(table.someAccountAttribute).toBeDefined()\n    })\n\n    test(\"Custom session model\", async () => {\n      const table = await sequelize\n        .getQueryInterface()\n        .describeTable(\"sessions\")\n\n      expect(table.someSessionAttribute).toBeDefined()\n    })\n\n    test(\"Custom verification_token model\", async () => {\n      const table = await sequelize\n        .getQueryInterface()\n        .describeTable(\"verification_tokens\")\n\n      expect(table.someVerificationTokenAttribute).toBeDefined()\n    })\n  })\n})\n"
  },
  {
    "path": "packages/adapter-sequelize/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-sequelize/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\", \"src/models.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/sequelize-adapter\",\n  entryFileName: \"../sequelize-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-supabase/.gitignore",
    "content": "supabase/.temp\nsupabase/.branches\nsupabase/functions\n"
  },
  {
    "path": "packages/adapter-supabase/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://supabase.com\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/supabase.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Supabase Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/supabase-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/supabase-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/supabase-adapter?color=green&label=@auth/supabase-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/supabase-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/supabase-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/supabase).\n"
  },
  {
    "path": "packages/adapter-supabase/package.json",
    "content": "{\n  \"name\": \"@auth/supabase-adapter\",\n  \"version\": \"1.11.0\",\n  \"description\": \"Supabase adapter for Auth.js\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Martin Sonnberger <martin.sonnberger@icloud.com>\",\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"supabase\"\n  ],\n  \"license\": \"ISC\",\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"tsc\",\n    \"clean\": \"rm -rf *.d.ts* *.js dist/ coverage/ supabase/{.branches,.temp}\",\n    \"test\": \"./test/test.sh\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"@supabase/supabase-js\": \"^2.43.1\"\n  },\n  \"devDependencies\": {\n    \"@supabase/supabase-js\": \"^2.43.1\",\n    \"supabase\": \"^1.165.0\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-supabase/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>Official <a href=\"https://supabase.com/docs\">Supabase</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://supabase.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/supabase.svg\" width=\"50\"/>\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @supabase/supabase-js @auth/supabase-adapter\n * ```\n *\n * @module @auth/supabase-adapter\n */\nimport { createClient } from \"@supabase/supabase-js\"\nimport {\n  type Adapter,\n  type AdapterSession,\n  type AdapterUser,\n  type VerificationToken,\n  isDate,\n} from \"@auth/core/adapters\"\n\nexport function format<T>(obj: Record<string, any>): T {\n  for (const [key, value] of Object.entries(obj)) {\n    if (value === null) {\n      delete obj[key]\n    }\n\n    if (isDate(value)) {\n      obj[key] = new Date(value)\n    }\n  }\n\n  return obj as T\n}\n\n/**\n * This is the interface of the Supabase adapter options.\n **/\nexport interface SupabaseAdapterOptions {\n  /**\n   * The URL of your Supabase database\n   **/\n  url: string\n  /**\n   * The secret to grant access to the database\n   **/\n  secret: string\n}\n\nexport function SupabaseAdapter(options: SupabaseAdapterOptions): Adapter {\n  const { url, secret } = options\n  const supabase = createClient<Database, \"next_auth\">(url, secret, {\n    db: { schema: \"next_auth\" },\n    global: { headers: { \"X-Client-Info\": \"@auth/supabase-adapter\" } },\n    auth: { persistSession: false },\n  })\n  return {\n    async createUser(user) {\n      const { data, error } = await supabase\n        .from(\"users\")\n        .insert({\n          ...user,\n          emailVerified: user.emailVerified?.toISOString(),\n        })\n        .select()\n        .single()\n\n      if (error) throw error\n\n      return format<AdapterUser>(data)\n    },\n    async getUser(id) {\n      const { data, error } = await supabase\n        .from(\"users\")\n        .select()\n        .eq(\"id\", id)\n        .maybeSingle()\n\n      if (error) throw error\n      if (!data) return null\n\n      return format<AdapterUser>(data)\n    },\n    async getUserByEmail(email) {\n      const { data, error } = await supabase\n        .from(\"users\")\n        .select()\n        .eq(\"email\", email)\n        .maybeSingle()\n\n      if (error) throw error\n      if (!data) return null\n\n      return format<AdapterUser>(data)\n    },\n    async getUserByAccount({ providerAccountId, provider }) {\n      const { data, error } = await supabase\n        .from(\"accounts\")\n        .select(\"users (*)\")\n        .match({ provider, providerAccountId })\n        .maybeSingle()\n\n      if (error) throw error\n      if (!data || !data.users) return null\n\n      return format<AdapterUser>(data.users)\n    },\n    async updateUser(user) {\n      const { data, error } = await supabase\n        .from(\"users\")\n        .update({\n          ...user,\n          emailVerified: user.emailVerified?.toISOString(),\n        })\n        .eq(\"id\", user.id)\n        .select()\n        .single()\n\n      if (error) throw error\n\n      return format<AdapterUser>(data)\n    },\n    async deleteUser(userId) {\n      const { error } = await supabase.from(\"users\").delete().eq(\"id\", userId)\n\n      if (error) throw error\n    },\n    async linkAccount(account) {\n      const { error } = await supabase.from(\"accounts\").insert(account)\n\n      if (error) throw error\n    },\n    async unlinkAccount({ providerAccountId, provider }) {\n      const { error } = await supabase\n        .from(\"accounts\")\n        .delete()\n        .match({ provider, providerAccountId })\n\n      if (error) throw error\n    },\n    async createSession({ sessionToken, userId, expires }) {\n      const { data, error } = await supabase\n        .from(\"sessions\")\n        .insert({ sessionToken, userId, expires: expires.toISOString() })\n        .select()\n        .single()\n\n      if (error) throw error\n\n      return format<AdapterSession>(data)\n    },\n    async getSessionAndUser(sessionToken) {\n      const { data, error } = await supabase\n        .from(\"sessions\")\n        .select(\"*, users(*)\")\n        .eq(\"sessionToken\", sessionToken)\n        .maybeSingle()\n\n      if (error) throw error\n      if (!data) return null\n\n      const { users: user, ...session } = data\n\n      return {\n        user: format<AdapterUser>(\n          user as Database[\"next_auth\"][\"Tables\"][\"users\"][\"Row\"][]\n        ),\n        session: format<AdapterSession>(session),\n      }\n    },\n    async updateSession(session) {\n      const { data, error } = await supabase\n        .from(\"sessions\")\n        .update({\n          ...session,\n          expires: session.expires?.toISOString(),\n        })\n        .eq(\"sessionToken\", session.sessionToken)\n        .select()\n        .single()\n\n      if (error) throw error\n\n      return format<AdapterSession>(data)\n    },\n    async deleteSession(sessionToken) {\n      const { error } = await supabase\n        .from(\"sessions\")\n        .delete()\n        .eq(\"sessionToken\", sessionToken)\n\n      if (error) throw error\n    },\n    async createVerificationToken(token) {\n      const { data, error } = await supabase\n        .from(\"verification_tokens\")\n        .insert({\n          ...token,\n          expires: token.expires.toISOString(),\n        })\n        .select()\n        .single()\n\n      if (error) throw error\n\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n      const { id, ...verificationToken } = data\n\n      return format<VerificationToken>(verificationToken)\n    },\n    async useVerificationToken({ identifier, token }) {\n      const { data, error } = await supabase\n        .from(\"verification_tokens\")\n        .delete()\n        .match({ identifier, token })\n        .select()\n        .maybeSingle()\n\n      if (error) throw error\n      if (!data) return null\n\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n      const { id, ...verificationToken } = data\n\n      return format<VerificationToken>(verificationToken)\n    },\n  }\n}\n\ninterface Database {\n  next_auth: {\n    Tables: {\n      accounts: {\n        Row: {\n          id: string\n          type: string | null\n          provider: string | null\n          providerAccountId: string | null\n          refresh_token: string | null\n          access_token: string | null\n          expires_at: number | null\n          token_type: string | null\n          scope: string | null\n          id_token: string | null\n          session_state: string | null\n          oauth_token_secret: string | null\n          oauth_token: string | null\n          userId: string | null\n        }\n        Insert: {\n          id?: string\n          type?: string | null\n          provider?: string | null\n          providerAccountId?: string | null\n          refresh_token?: string | null\n          access_token?: string | null\n          expires_at?: number | null\n          token_type?: string | null\n          scope?: string | null\n          id_token?: string | null\n          session_state?: string | null\n          oauth_token_secret?: string | null\n          oauth_token?: string | null\n          userId?: string | null\n        }\n        Update: {\n          id?: string\n          type?: string | null\n          provider?: string | null\n          providerAccountId?: string | null\n          refresh_token?: string | null\n          access_token?: string | null\n          expires_at?: number | null\n          token_type?: string | null\n          scope?: string | null\n          id_token?: string | null\n          session_state?: string | null\n          oauth_token_secret?: string | null\n          oauth_token?: string | null\n          userId?: string | null\n        }\n      }\n      sessions: {\n        Row: {\n          expires: string | null\n          sessionToken: string | null\n          userId: string | null\n          id: string\n        }\n        Insert: {\n          expires?: string | null\n          sessionToken?: string | null\n          userId?: string | null\n          id?: string\n        }\n        Update: {\n          expires?: string | null\n          sessionToken?: string | null\n          userId?: string | null\n          id?: string\n        }\n      }\n      users: {\n        Row: {\n          name: string | null\n          email: string | null\n          emailVerified: string | null\n          image: string | null\n          id: string\n        }\n        Insert: {\n          name?: string | null\n          email?: string | null\n          emailVerified?: string | null\n          image?: string | null\n          id?: string\n        }\n        Update: {\n          name?: string | null\n          email?: string | null\n          emailVerified?: string | null\n          image?: string | null\n          id?: string\n        }\n      }\n      verification_tokens: {\n        Row: {\n          id: number\n          identifier: string | null\n          token: string | null\n          expires: string | null\n        }\n        Insert: {\n          id?: number\n          identifier?: string | null\n          token?: string | null\n          expires?: string | null\n        }\n        Update: {\n          id?: number\n          identifier?: string | null\n          token?: string | null\n          expires?: string | null\n        }\n      }\n    }\n    Views: {\n      [_ in never]: never\n    }\n    Functions: {\n      uid: {\n        Args: Record<PropertyKey, never>\n        Returns: string\n      }\n    }\n    Enums: {\n      [_ in never]: never\n    }\n  }\n}\n"
  },
  {
    "path": "packages/adapter-supabase/supabase/config.toml",
    "content": "# A string used to distinguish different Supabase projects on the same host. Defaults to the working\n# directory name when running `supabase init`.\nproject_id = \"nextauth\"\n\n[api]\n# Port to use for the API URL.\nport = 54321\n# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API\n# endpoints. public and storage are always included.\nschemas = [\"next_auth\"]\n# Extra schemas to add to the search_path of every request.\nextra_search_path = [\"extensions\"]\n# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size\n# for accidental or malicious requests.\nmax_rows = 1000\n\n[db]\n# Port to use for the local database URL.\nport = 54322\n# The database major version to use. This has to be the same as your remote database's. Run `SHOW\n# server_version;` on the remote database to check.\nmajor_version = 14\n\n[db.pooler]\nenabled = true\n\n[realtime]\nenabled = false\n\n[studio]\nenabled = false\n# Port to use for Supabase Studio.\nport = 54323\n\n[auth]\nenabled = false\n# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used\n# in emails.\nsite_url = \"http://localhost:3000\"\n# A list of *exact* URLs that auth providers are permitted to redirect to post authentication.\nadditional_redirect_urls = [\"https://localhost:3000\"]\n# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 seconds (one\n# week).\njwt_expiry = 3600\n# Allow/disallow new user signups to your project.\nenable_signup = true\n\n[inbucket]\nenabled = false\n\n[storage]\nenabled = false\n\n[analytics]\nenabled = false\n"
  },
  {
    "path": "packages/adapter-supabase/supabase/migrations/20221108043803_create_next_auth_schema.sql",
    "content": "--\n-- Name: next_auth; Type: SCHEMA;\n--\nCREATE SCHEMA next_auth;\n\nGRANT USAGE ON SCHEMA next_auth TO service_role;\nGRANT ALL ON SCHEMA next_auth TO postgres;\n\n--\n-- Create users table\n--\nCREATE TABLE IF NOT EXISTS next_auth.users\n(\n    id uuid NOT NULL DEFAULT uuid_generate_v4(),\n    name text,\n    email text,\n    \"emailVerified\" timestamp with time zone,\n    image text,\n    CONSTRAINT users_pkey PRIMARY KEY (id),\n    CONSTRAINT email_unique UNIQUE (email)\n);\n\nGRANT ALL ON TABLE next_auth.users TO postgres;\nGRANT ALL ON TABLE next_auth.users TO service_role;\n\n--- uid() function to be used in RLS policies\nCREATE FUNCTION next_auth.uid() RETURNS uuid\n    LANGUAGE sql STABLE\n    AS $$\n  select \n  \tcoalesce(\n\t\tnullif(current_setting('request.jwt.claim.sub', true), ''),\n\t\t(nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'sub')\n\t)::uuid\n$$;\n\n--\n-- Create sessions table\n--\nCREATE TABLE IF NOT EXISTS  next_auth.sessions\n(\n    id uuid NOT NULL DEFAULT uuid_generate_v4(),\n    expires timestamp with time zone NOT NULL,\n    \"sessionToken\" text NOT NULL,\n    \"userId\" uuid,\n    CONSTRAINT sessions_pkey PRIMARY KEY (id),\n    CONSTRAINT sessionToken_unique UNIQUE (\"sessionToken\"),\n    CONSTRAINT \"sessions_userId_fkey\" FOREIGN KEY (\"userId\")\n        REFERENCES  next_auth.users (id) MATCH SIMPLE\n        ON UPDATE NO ACTION\n        ON DELETE CASCADE\n);\n\nGRANT ALL ON TABLE next_auth.sessions TO postgres;\nGRANT ALL ON TABLE next_auth.sessions TO service_role;\n\n--\n-- Create accounts table\n--\nCREATE TABLE IF NOT EXISTS  next_auth.accounts\n(\n    id uuid NOT NULL DEFAULT uuid_generate_v4(),\n    type text NOT NULL,\n    provider text NOT NULL,\n    \"providerAccountId\" text NOT NULL,\n    refresh_token text,\n    access_token text,\n    expires_at bigint,\n    token_type text,\n    scope text,\n    id_token text,\n    session_state text,\n    oauth_token_secret text,\n    oauth_token text,\n    \"userId\" uuid,\n    CONSTRAINT accounts_pkey PRIMARY KEY (id),\n    CONSTRAINT provider_unique UNIQUE (provider, \"providerAccountId\"),\n    CONSTRAINT \"accounts_userId_fkey\" FOREIGN KEY (\"userId\")\n        REFERENCES  next_auth.users (id) MATCH SIMPLE\n        ON UPDATE NO ACTION\n        ON DELETE CASCADE\n);\n\nGRANT ALL ON TABLE next_auth.accounts TO postgres;\nGRANT ALL ON TABLE next_auth.accounts TO service_role;\n\n--\n-- Create verification_tokens table\n--\nCREATE TABLE IF NOT EXISTS  next_auth.verification_tokens\n(\n    identifier text,\n    token text,\n    expires timestamp with time zone NOT NULL,\n    CONSTRAINT verification_tokens_pkey PRIMARY KEY (token),\n    CONSTRAINT token_unique UNIQUE (token),\n    CONSTRAINT token_identifier_unique UNIQUE (token, identifier)\n);\n\nGRANT ALL ON TABLE next_auth.verification_tokens TO postgres;\nGRANT ALL ON TABLE next_auth.verification_tokens TO service_role;"
  },
  {
    "path": "packages/adapter-supabase/supabase/migrations/20221108044627_create_public_users_table.sql",
    "content": "/** \n* USERS\n* Note: This table contains user data. Users should only be able to view and update their own data.\n*/\ncreate table users (\n  -- UUID from next_auth.users\n  id uuid not null primary key,\n  name text,\n  email text,\n  image text,\n  constraint \"users_id_fkey\" foreign key (\"id\")\n        references  next_auth.users (id) match simple\n        on update no action\n        on delete cascade -- if user is deleted in NextAuth they will also be deleted in our public table.\n);\nalter table users enable row level security;\ncreate policy \"Can view own user data.\" on users for select using (next_auth.uid() = id);\ncreate policy \"Can update own user data.\" on users for update using (next_auth.uid() = id);\n\n/**\n* This trigger automatically creates a user entry when a new user signs up via NextAuth.\n*/ \ncreate function public.handle_new_user() \nreturns trigger as $$\nbegin\n  insert into public.users (id, name, email, image)\n  values (new.id, new.name, new.email, new.image);\n  return new;\nend;\n$$ language plpgsql security definer;\ncreate trigger on_auth_user_created\n  after insert on next_auth.users\n  for each row execute procedure public.handle_new_user();"
  },
  {
    "path": "packages/adapter-supabase/test/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { format, SupabaseAdapter } from \"../src\"\nimport { createClient } from \"@supabase/supabase-js\"\nimport type {\n  AdapterSession,\n  AdapterUser,\n  VerificationToken,\n} from \"@auth/core/adapters\"\nimport type { Account } from \"@auth/core/types\"\n\nconst url = process.env.SUPABASE_URL ?? \"http://127.0.0.1:54321\"\nconst secret =\n  process.env.SUPABASE_SERVICE_ROLE_KEY ||\n  \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSJ9.vI9obAHOGyVVKa3pD--kJlyxp-Z2zV9UUMAhKpNLAcU\"\n\nconst supabase = createClient(url, secret, {\n  db: { schema: \"next_auth\" },\n})\n\nrunBasicTests({\n  adapter: SupabaseAdapter({ url, secret }),\n  db: {\n    async session(sessionToken) {\n      const { data, error } = await supabase\n        .from(\"sessions\")\n        .select()\n        .eq(\"sessionToken\", sessionToken)\n        .maybeSingle()\n\n      if (error) throw error\n      if (!data) return null\n\n      return format<AdapterSession>(data)\n    },\n    async user(id) {\n      const { data, error } = await supabase\n        .from(\"users\")\n        .select()\n        .eq(\"id\", id)\n        .maybeSingle()\n\n      if (error) throw error\n      if (!data) return null\n\n      return format<AdapterUser>(data)\n    },\n    async account({ provider, providerAccountId }) {\n      const { data, error } = await supabase\n        .from(\"accounts\")\n        .select()\n        .match({ provider, providerAccountId })\n        .maybeSingle()\n\n      if (error) throw error\n      if (!data) return null\n\n      return format<Account>(data)\n    },\n    async verificationToken({ identifier, token }) {\n      const { data, error } = await supabase\n        .from(\"verification_tokens\")\n        .select()\n        .match({ identifier, token })\n        .single()\n\n      if (error) throw error\n\n      const { id, ...verificationToken } = data\n\n      return format<VerificationToken>(verificationToken)\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-supabase/test/test.sh",
    "content": "#!/usr/bin/env bash\n\n# Start database and apply migrations\npnpm exec supabase start\n\nprintf \"\\nWaiting 10s for db to start...\" && sleep 10\n\n# Always stop Supabase, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  pnpm exec supabase stop --no-backup\nelse\n  pnpm exec supabase stop --no-backup && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-supabase/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-supabase/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/supabase-adapter\",\n  entryFileName: \"../supabase-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-surrealdb/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://surrealdb.com/\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/surrealdb.png\"/>\n  </a>\n  <h3 align=\"center\"><b>Surreal DB Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/surrealdb-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/surrealdb-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/surrealdb-adapter?color=green&label=@auth/surrealdb-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/surrealdb-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/surrealdb-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/surrealdb).\n"
  },
  {
    "path": "packages/adapter-surrealdb/package.json",
    "content": "{\n  \"name\": \"@auth/surrealdb-adapter\",\n  \"version\": \"2.2.0\",\n  \"description\": \"SurrealDB adapter for next-auth.\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Dylan Vanmali <https://github.com/dvanmali>\",\n  \"contributors\": [\n    \"Martin Schaer <martin@schaerweb.com>\",\n    \"Thang Huu Vu <hi@thvu.dev>\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"surrealdb\",\n    \"adapter\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"test\": \"./test/test.sh\",\n    \"build\": \"tsc\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"surrealdb\": \"^1.3.0\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-surrealdb/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>Official <a href=\"https://www.surrealdb.com\">SurrealDB</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://www.surrealdb.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/surrealdb.svg\" width=\"30\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/surrealdb-adapter surrealdb.js\n * ```\n *\n * @module @auth/surrealdb-adapter\n */\nimport { Surreal, RecordId } from \"surrealdb\"\nimport type {\n  Adapter,\n  AdapterUser,\n  AdapterAccount,\n  AdapterAccountType,\n  AdapterAuthenticator,\n  AdapterSession,\n  VerificationToken,\n} from \"@auth/core/adapters\"\n\ntype Document<T = RecordId<string>> = {\n  id: T\n  [key: string]: unknown\n}\nexport type UserDoc = Document<RecordId<\"user\">> & {\n  email: string\n  emailVerified?: string | Date\n}\nexport type AccountDoc<T = RecordId<\"user\">> = Document<RecordId<\"account\">> & {\n  userId: T\n  refresh_token?: string\n  access_token?: string\n  type: AdapterAccountType\n  provider: string\n  providerAccountId: string\n  expires_at?: number\n}\nexport type SessionDoc<T = RecordId<\"user\">> = Document<RecordId<\"session\">> & {\n  userId: T\n  expires: string | Date\n  sessionToken: string\n}\nexport type VerificationTokenDoc = Document<RecordId<\"verification_token\">> & {\n  identifier: string\n  expires: Date\n  token: string\n}\nexport type AuthenticatorDoc<T = RecordId<\"user\">> = Document<\n  RecordId<\"authenticator\">\n> &\n  Omit<AdapterAuthenticator, \"userId\"> & {\n    userId: T\n    counter: number\n  }\n\n/** @internal */\n// Convert DB object to AdapterUser\nexport const docToUser = (doc: UserDoc): AdapterUser => ({\n  ...doc,\n  id: doc.id.id.toString(),\n  emailVerified:\n    typeof doc?.emailVerified === \"string\"\n      ? new Date(Date.parse(doc.emailVerified))\n      : (doc?.emailVerified ?? null),\n})\n\n/** @internal */\n// Convert DB object to AdapterAccount\nexport const docToAccount = (doc: AccountDoc): AdapterAccount => ({\n  ...doc,\n  id: doc.id.id.toString(),\n  userId: doc.userId.id.toString(),\n})\n\n/** @internal */\n// Convert DB object to AdapterAccount\nexport const docToAuthenticator = (\n  doc: AuthenticatorDoc\n): AdapterAuthenticator => ({\n  ...doc,\n  userId: doc.userId.id.toString(),\n})\n\n/** @internal */\n// Convert DB object to AdapterSession\nexport const docToSession = (\n  doc: SessionDoc<RecordId<string> | UserDoc>\n): AdapterSession => ({\n  userId:\n    doc.userId instanceof RecordId\n      ? doc.userId.id.toString()\n      : doc.userId.id.id.toString(),\n  expires:\n    typeof doc?.expires === \"string\"\n      ? new Date(Date.parse(doc.expires))\n      : (doc?.expires ?? null),\n  sessionToken: doc.sessionToken,\n})\n\n/** @internal */\n// Convert DB object to Verification Token\nexport const docToVerificationToken = (\n  doc: VerificationTokenDoc\n): VerificationToken => ({\n  identifier: doc.identifier,\n  expires: doc.expires,\n  token: doc.token,\n})\n\n/** @internal */\n// Convert AdapterUser to DB object\nconst userToDoc = (user: Partial<AdapterUser>): Partial<UserDoc> => ({\n  ...user,\n  id: user?.id ? new RecordId(\"user\", user.id) : undefined,\n  emailVerified: user.emailVerified ?? undefined,\n})\n\n/** @internal */\n// Convert AdapterAccount to DB object\nconst accountToDoc = (account: AdapterAccount): Omit<AccountDoc, \"id\"> => ({\n  ...account,\n  userId: new RecordId(\"user\", account.userId.replace(\"user:\", \"\")),\n})\n\n/** @internal */\n// Convert AdapterAuthenticator to DB object\nconst authenticatorToDoc = (\n  authenticator: AdapterAuthenticator\n): Partial<AuthenticatorDoc> => ({\n  ...authenticator,\n  userId: new RecordId(\"user\", authenticator.userId.replace(\"user:\", \"\")),\n})\n\n/** @internal */\n// Convert AdapterSession to DB object\nexport const sessionToDoc = (\n  session: AdapterSession\n): Partial<SessionDoc<RecordId<string>>> => ({\n  ...session,\n  userId: new RecordId(\"user\", session.userId.replace(\"user:\", \"\")),\n  expires: session.expires,\n})\n\n/** @internal */\n// Convert VerificationToken to DB object\nconst verificationTokenToDoc = (\n  account: VerificationToken\n): Omit<VerificationTokenDoc, \"id\"> => ({\n  ...account,\n})\n\n/** @internal */\n/**\n * Removes all undefined fields in an object\n * @param obj\n * @returns\n */\nfunction removeUndefinedFields<T>(obj: T): Partial<T | null> {\n  if (typeof obj !== \"object\" || obj === null) {\n    return obj\n  }\n  for (const key in obj) {\n    if (obj[key] === undefined) {\n      delete obj[key]\n    } else if (typeof obj[key] === \"object\" && obj[key] !== null) {\n      removeUndefinedFields(obj[key])\n    }\n  }\n  return obj\n}\n\nexport function SurrealDBAdapter(\n  client: Promise<Surreal>\n  // options = {}\n): Adapter {\n  return {\n    async createUser(user: Partial<AdapterUser>) {\n      try {\n        const surreal = await client\n        const doc = userToDoc(user)\n        const userDoc = await surreal.create<UserDoc, Omit<UserDoc, \"id\">>(\n          \"user\",\n          doc\n        )\n        if (userDoc.length) {\n          return docToUser(userDoc[0])\n        }\n      } catch {}\n      throw new Error(\"User not created\")\n    },\n    async getUser(id: string) {\n      const surreal = await client\n      try {\n        const [userDoc] = await surreal.query<[UserDoc[]]>(\n          \"SELECT * FROM $user\",\n          {\n            user: new RecordId(\"user\", id),\n          }\n        )\n        const doc = userDoc.at(0)\n        if (doc) {\n          return docToUser(doc)\n        }\n      } catch {}\n      return null\n    },\n    async getUserByEmail(email: string) {\n      const surreal = await client\n      try {\n        const [users] = await surreal.query<[UserDoc[]]>(\n          `SELECT * FROM user WHERE email = $email`,\n          { email }\n        )\n        const doc = users.at(0)\n        if (doc) return docToUser(doc)\n      } catch {}\n      return null\n    },\n    async getUserByAccount({\n      providerAccountId,\n      provider,\n    }: Pick<AdapterAccount, \"provider\" | \"providerAccountId\">) {\n      const surreal = await client\n      try {\n        const [accounts] = await surreal.query<[AccountDoc<UserDoc>[]]>(\n          `SELECT userId FROM account WHERE providerAccountId = $providerAccountId AND provider = $provider FETCH userId`,\n          {\n            providerAccountId,\n            provider,\n          }\n        )\n        const user = accounts.at(0)?.userId\n        if (user) return docToUser(user)\n      } catch {}\n      return null\n    },\n    async updateUser(user: Partial<AdapterUser>) {\n      try {\n        if (!user.id) throw new Error(\"User id is required\")\n        const surreal = await client\n        const doc: Partial<UserDoc> | null = removeUndefinedFields(\n          userToDoc({\n            ...user,\n            id: undefined,\n          })\n        )\n        if (doc) {\n          const updatedUser = await surreal.merge<UserDoc, Partial<UserDoc>>(\n            new RecordId(\"user\", user.id),\n            doc\n          )\n          if (updatedUser) {\n            return docToUser(updatedUser)\n          }\n        }\n      } catch {}\n      throw new Error(\"User not updated\")\n    },\n    async deleteUser(userId: string) {\n      const surreal = await client\n\n      // delete account\n      try {\n        const [accounts] = await surreal.query<[AccountDoc[]]>(\n          `SELECT * FROM account WHERE userId = $userId LIMIT 1`,\n          { userId: new RecordId(\"user\", userId) }\n        )\n        const account = accounts.at(0)\n        if (account) {\n          await surreal.delete(account.id)\n        }\n      } catch {}\n\n      // delete session\n      try {\n        const [sessions] = await surreal.query<[SessionDoc[]]>(\n          `SELECT * FROM session WHERE userId = $userId LIMIT 1`,\n          { userId: new RecordId(\"user\", userId) }\n        )\n        const session = sessions.at(0)\n        if (session) {\n          await surreal.delete(session.id)\n        }\n      } catch {}\n\n      // delete user\n      await surreal.delete(new RecordId(\"user\", userId))\n\n      // TODO: put all 3 deletes inside a Promise all\n    },\n    async linkAccount(account: AdapterAccount) {\n      try {\n        const surreal = await client\n        const accountDoc = await surreal.create<\n          AccountDoc,\n          Omit<AccountDoc, \"id\">\n        >(\"account\", accountToDoc(account))\n        if (accountDoc.length) {\n          return docToAccount(accountDoc[0])\n        }\n      } catch {}\n      throw new Error(\"Account not created\")\n    },\n    async unlinkAccount({\n      providerAccountId,\n      provider,\n    }: Pick<AdapterAccount, \"provider\" | \"providerAccountId\">) {\n      const surreal = await client\n      try {\n        const [accounts] = await surreal.query<[AccountDoc[]]>(\n          `SELECT * FROM account WHERE providerAccountId = $providerAccountId AND provider = $provider LIMIT 1`,\n          { providerAccountId, provider }\n        )\n        const account = accounts.at(0)\n        if (account) {\n          await surreal.delete(account.id)\n        }\n      } catch {}\n    },\n    async createSession({\n      sessionToken,\n      userId,\n      expires,\n    }: {\n      sessionToken: string\n      userId: string\n      expires: Date\n    }) {\n      const surreal = await client\n      const doc = sessionToDoc({\n        sessionToken,\n        userId,\n        expires,\n      })\n      const result = await surreal.create<SessionDoc, Omit<SessionDoc, \"id\">>(\n        \"session\",\n        doc\n      )\n      return docToSession(result[0]) ?? null\n    },\n    async getSessionAndUser(sessionToken: string) {\n      const surreal = await client\n      try {\n        // Can't use limit 1 because it prevents userId from being fetched.\n        //   Works setting limit to 2\n        const [sessions] = await surreal.query<[SessionDoc<UserDoc>[]]>(\n          `SELECT * FROM session WHERE sessionToken = $sessionToken FETCH userId`,\n          { sessionToken }\n        )\n        const session = sessions.at(0)\n        if (session) {\n          const userDoc = session.userId\n          if (!userDoc) return null\n          return {\n            user: docToUser(userDoc),\n            session: docToSession({\n              ...session,\n              userId: userDoc.id,\n            }),\n          }\n        }\n      } catch {}\n      return null\n    },\n    async updateSession(\n      session: Partial<AdapterSession> & Pick<AdapterSession, \"sessionToken\">\n    ) {\n      const surreal = await client\n      try {\n        const [sessions] = await surreal.query<[SessionDoc[]]>(\n          `SELECT * FROM session WHERE sessionToken = $sessionToken LIMIT 1`,\n          {\n            sessionToken: session.sessionToken,\n          }\n        )\n        const sessionDoc = sessions.at(0)\n        if (sessionDoc && session.expires) {\n          const sessionId = sessionDoc.id\n          const doc: Partial<SessionDoc<RecordId<string>>> | null =\n            removeUndefinedFields(\n              sessionToDoc({\n                ...sessionDoc,\n                ...session,\n                userId: sessionDoc.userId.toString(),\n                expires: session.expires,\n              })\n            )\n          if (doc) {\n            const updatedSession = await surreal.merge<\n              SessionDoc,\n              Partial<SessionDoc<RecordId<string>>>\n            >(sessionId, doc)\n            if (updatedSession?.id) {\n              return docToSession(updatedSession)\n            }\n          }\n        }\n      } catch {}\n      return null\n    },\n    async deleteSession(sessionToken: string) {\n      const surreal = await client\n      try {\n        const [sessions] = await surreal.query<[SessionDoc[]]>(\n          `SELECT * FROM session WHERE sessionToken = $sessionToken LIMIT 1`,\n          {\n            sessionToken,\n          }\n        )\n        const session = sessions.at(0)\n        if (session) {\n          await surreal.delete(session.id)\n          return\n        }\n      } catch {}\n    },\n    async createVerificationToken(verificationToken: VerificationToken) {\n      try {\n        const surreal = await client\n        const doc = verificationTokenToDoc(verificationToken)\n\n        const verificationTokenDocs = await surreal.create<\n          VerificationTokenDoc,\n          Omit<VerificationTokenDoc, \"id\">\n        >(\"verification_token\", doc)\n        if (verificationTokenDocs.length) {\n          const verificationTokenDoc: Partial<VerificationTokenDoc> =\n            verificationTokenDocs[0]\n          if (verificationTokenDoc.id) delete verificationTokenDoc.id\n          return docToVerificationToken(\n            verificationTokenDoc as VerificationTokenDoc\n          )\n        }\n      } catch {}\n      throw new Error(\"Verification Token not created\")\n    },\n    async useVerificationToken({\n      identifier,\n      token,\n    }: {\n      identifier: string\n      token: string\n    }) {\n      const surreal = await client\n      try {\n        const [tokens] = await surreal.query<[VerificationTokenDoc[]]>(\n          `SELECT * FROM verification_token WHERE identifier = $identifier AND token = $vt LIMIT 1`,\n          { identifier, vt: token }\n        )\n        if (tokens.length && tokens.at(0)) {\n          const vt = tokens[0]\n          if (vt) {\n            await surreal.delete(vt.id)\n            const verificationTokenDoc: Partial<VerificationTokenDoc> = vt\n            if (verificationTokenDoc.id) delete verificationTokenDoc.id\n            return docToVerificationToken(\n              verificationTokenDoc as VerificationTokenDoc\n            )\n          }\n        } else {\n          return null\n        }\n      } catch {}\n      throw new Error(\"Verification Token not used\")\n    },\n    async getAccount(\n      providerAccountId: AdapterAccount[\"providerAccountId\"],\n      provider: AdapterAccount[\"provider\"]\n    ) {\n      const surreal = await client\n      try {\n        const [accountsDoc] = await surreal.query<[AccountDoc[]]>(\n          `SELECT * FROM account WHERE providerAccountId = $pid AND provider = $provider LIMIT 1`,\n          {\n            pid: providerAccountId,\n            provider,\n          }\n        )\n        if (accountsDoc.length) {\n          return docToAccount(accountsDoc[0])\n        }\n      } catch {}\n      return null\n    },\n    async createAuthenticator(authenticator: AdapterAuthenticator) {\n      try {\n        const surreal = await client\n        const authenticatorDoc = await surreal.create<\n          AuthenticatorDoc,\n          Omit<AuthenticatorDoc, \"id\">\n        >(\"authenticator\", authenticatorToDoc(authenticator))\n        if (authenticatorDoc.length) {\n          return docToAuthenticator(authenticatorDoc[0])\n        }\n      } catch {}\n      throw new Error(\"Authenticator not created\")\n    },\n    async getAuthenticator(credentialId: AdapterAuthenticator[\"credentialID\"]) {\n      const surreal = await client\n      try {\n        const [authenticatorDoc] = await surreal.query<[AuthenticatorDoc[]]>(\n          `SELECT * FROM authenticator WHERE credentialID = $cid LIMIT 1`,\n          {\n            cid: credentialId,\n          }\n        )\n        if (authenticatorDoc.length) {\n          return docToAuthenticator(authenticatorDoc[0])\n        }\n      } catch {}\n      return null\n    },\n    async listAuthenticatorsByUserId(userId: AdapterAuthenticator[\"userId\"]) {\n      const surreal = await client\n      try {\n        const [authenticatorDocs] = await surreal.query<[AuthenticatorDoc[]]>(\n          `SELECT * FROM authenticator WHERE userId = $userId LIMIT 1`,\n          {\n            userId,\n          }\n        )\n\n        return authenticatorDocs.map((v) => docToAuthenticator(v))\n      } catch {}\n      throw new Error(\"Verification Token not found\")\n    },\n    async updateAuthenticatorCounter(\n      credentialId: AdapterAuthenticator[\"credentialID\"],\n      newCounter: AdapterAuthenticator[\"counter\"]\n    ) {\n      try {\n        if (!credentialId) throw new Error(\"credential id is required\")\n        const surreal = await client\n        const [authenticatorDoc] = await surreal.query<[AuthenticatorDoc]>(\n          `UPDATE ONLY authenticator MERGE $doc WHERE credentialID = $cid`,\n          {\n            cid: credentialId,\n            doc: {\n              counter: newCounter,\n            },\n          }\n        )\n        return docToAuthenticator(authenticatorDoc)\n      } catch {}\n      throw Error(\n        `Unable to update authenticator with credential ${credentialId}`\n      )\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-surrealdb/test/common.ts",
    "content": "import { RecordId, Surreal } from \"surrealdb\"\n\nimport {\n  SurrealDBAdapter,\n  docToUser,\n  docToAccount,\n  docToSession,\n  docToVerificationToken,\n  docToAuthenticator,\n} from \"../src/index\"\nimport type {\n  UserDoc,\n  AccountDoc,\n  SessionDoc,\n  VerificationTokenDoc,\n  AuthenticatorDoc,\n} from \"../src/index\"\n\nexport const config = (clientPromise: Promise<Surreal>) => ({\n  adapter: SurrealDBAdapter(clientPromise),\n  testWebAuthnMethods: true,\n  db: {\n    // Generates a guid like surrealdb\n    id() {\n      const length = 20\n      const charset =\n        \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n      const charsetLength = charset.length\n      const result: string[] = []\n      const byteRange = 256 // [0, 255)\n      const maxUsable = Math.floor(byteRange / charsetLength) * charsetLength\n\n      while (result.length < length) {\n        const randomBytes = new Uint8Array(length - result.length)\n        crypto.getRandomValues(randomBytes)\n        for (let i = 0; i < randomBytes.length && result.length < length; i++) {\n          if (randomBytes[i] < maxUsable) {\n            result.push(charset[randomBytes[i] % charsetLength])\n          }\n        }\n      }\n      return result.join(\"\")\n    },\n    connect: async () => {\n      const surreal = await clientPromise\n      await Promise.all([\n        surreal.delete(\"account\"),\n        surreal.delete(\"session\"),\n        surreal.delete(\"verification_token\"),\n        surreal.delete(\"user\"),\n        surreal.delete(\"authenticator\"),\n      ])\n    },\n    disconnect: async () => {\n      const surreal = await clientPromise\n      try {\n        await Promise.all([\n          surreal.delete(\"account\"),\n          surreal.delete(\"session\"),\n          surreal.delete(\"verification_token\"),\n          surreal.delete(\"user\"),\n          surreal.delete(\"authenticator\"),\n        ])\n      } catch (e) {\n        console.error(e)\n      }\n      if (surreal.close) surreal.close()\n    },\n    async user(id: string) {\n      const userId = new RecordId(\"user\", id)\n      const surreal = await clientPromise\n      const [users] = await surreal.query<[UserDoc[]]>(`SELECT * FROM $user`, {\n        user: userId,\n      })\n      const user = users.at(0)\n      if (user) return docToUser(user)\n      return null\n    },\n    async account({ provider, providerAccountId }) {\n      const surreal = await clientPromise\n      const [accounts] = await surreal.query<[AccountDoc[]]>(\n        `SELECT * FROM account WHERE provider = $provider AND providerAccountId = $providerAccountId`,\n        { provider, providerAccountId }\n      )\n      const account = accounts.at(0)\n      if (account) return docToAccount(account)\n      return null\n    },\n    async session(sessionToken: string) {\n      const surreal = await clientPromise\n      const [sessions] = await surreal.query<[SessionDoc[]]>(\n        `SELECT * FROM session WHERE sessionToken = $sessionToken`,\n        { sessionToken }\n      )\n      const session = sessions.at(0)\n      if (session) return docToSession(session)\n      return null\n    },\n    async verificationToken({ identifier, token }) {\n      const surreal = await clientPromise\n      const [tokens] = await surreal.query<[VerificationTokenDoc[]]>(\n        `SELECT * FROM verification_token WHERE identifier = $identifier AND token = $vt LIMIT 1`,\n        { identifier, vt: token }\n      )\n      const verificationToken: Partial<VerificationTokenDoc> | undefined =\n        tokens.at(0)\n      if (verificationToken) {\n        if (verificationToken.id) delete verificationToken.id\n        return docToVerificationToken(\n          verificationToken as Omit<VerificationTokenDoc, \"id\">\n        )\n      }\n      return null\n    },\n    async authenticator(credentialID: string) {\n      const surreal = await clientPromise\n      const [authenticators] = await surreal.query<[AuthenticatorDoc[]]>(\n        `SELECT * FROM authenticator WHERE credentialID = $credentialID`,\n        { credentialID }\n      )\n      const authenticator = authenticators.at(0)\n      if (authenticator) return docToAuthenticator(authenticator)\n      return null\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-surrealdb/test/index.test.ts",
    "content": "import { Surreal } from \"surrealdb\"\nimport { runBasicTests } from \"utils/adapter\"\n\nimport { config } from \"./common\"\n\nconst clientPromise = new Promise<Surreal>(async (resolve, reject) => {\n  try {\n    const db = new Surreal()\n    await db.connect(\"ws://0.0.0.0:8000\", {\n      namespace: \"test\",\n      database: \"test\",\n      auth: {\n        username: \"test\",\n        password: \"test\",\n      },\n    })\n    resolve(db)\n  } catch (e) {\n    reject(e)\n  }\n})\n\nrunBasicTests(config(clientPromise))\n"
  },
  {
    "path": "packages/adapter-surrealdb/test/test.sh",
    "content": "#!/usr/bin/env bash\n\nCONTAINER_NAME=authjs-surrealdb-test\n\n# Get the latest 2.x container since 3.x-alpha is not yet fully supported by js library\nTAG=$(curl -s \"https://registry.hub.docker.com/v2/repositories/surrealdb/surrealdb/tags?page_size=100\" \\\n| jq -r '.results[].name' \\\n| grep -E '^v2\\.[0-9]+(\\.[0-9]+)?$' \\\n| sort -V \\\n| tail -n1)\n\n# Start db\ndocker run -d --rm \\\n  -p 8000:8000 \\\n  --name ${CONTAINER_NAME} \\\n  surrealdb/surrealdb:${TAG} start --log debug --user test --pass test memory\n\necho \"Waiting 5s for db to start...\"\nsleep 5\n\n# Always stop container, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  docker stop ${CONTAINER_NAME}\nelse\n  docker stop ${CONTAINER_NAME} && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-surrealdb/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-surrealdb/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/surrealdb-adapter\",\n  entryFileName: \"../surrealdb-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-typeorm/.dockerignore",
    "content": "# Exclude directories we don't need from Docker context to improve build time\nnode_modules\nwww\nsrc"
  },
  {
    "path": "packages/adapter-typeorm/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://typeorm.io\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/typeorm.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>TypeORM Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/typeorm-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/typeorm-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/typeorm-adapter?color=green&label=@auth/typeorm-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/typeorm-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/typeorm-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/typeorm).\n"
  },
  {
    "path": "packages/adapter-typeorm/package.json",
    "content": "{\n  \"name\": \"@auth/typeorm-adapter\",\n  \"version\": \"2.11.0\",\n  \"description\": \"TypeORM adapter for Auth.js.\",\n  \"homepage\": \"https://authjs.dev/reference/adapter/typeorm\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Iain Collins\",\n  \"contributors\": [\n    \"Balázs Orbán <info@balazsorban.com>\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"typeorm\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"tsc\",\n    \"dev\": \"tsc -w\",\n    \"clean\": \"rm -rf dist *.js *.d.ts*\",\n    \"test\": \"pnpm test:mysql && pnpm test:sqlite && pnpm test:pg\",\n    \"test:mysql\": \"pnpm clean && ./test/mysql/test.sh\",\n    \"test:pg\": \"pnpm clean && ./test/postgresql/test.sh\",\n    \"test:sqlite\": \"pnpm clean && ./test/sqlite/test.sh\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"libsql\": \"^0.3.18\",\n    \"mssql\": \"^7.2.1\",\n    \"mysql\": \"^2.18.1\",\n    \"pg\": \"^8.7.3\",\n    \"typeorm\": \"0.3.17\",\n    \"typeorm-naming-strategies\": \"^4.1.0\"\n  },\n  \"peerDependencies\": {\n    \"libsql\": \"^0.3.18\",\n    \"mssql\": \"^6.2.1 || ^7 || ^8 || ^9\",\n    \"mysql\": \"^2.18.1 || ^3\",\n    \"pg\": \"^8.2.1\",\n    \"typeorm\": \"^0.3.7\"\n  },\n  \"peerDependenciesMeta\": {\n    \"libsql\": {\n      \"optional\": true\n    },\n    \"mysql\": {\n      \"optional\": true\n    },\n    \"mssql\": {\n      \"optional\": true\n    },\n    \"pg\": {\n      \"optional\": true\n    }\n  }\n}\n"
  },
  {
    "path": "packages/adapter-typeorm/src/entities.ts",
    "content": "import {\n  Entity,\n  PrimaryGeneratedColumn,\n  Column,\n  ManyToOne,\n  OneToMany,\n  ValueTransformer,\n} from \"typeorm\"\n\nconst transformer: Record<\"date\" | \"bigint\", ValueTransformer> = {\n  date: {\n    from: (date: string | null) => date && new Date(parseInt(date, 10)),\n    to: (date?: Date) => date?.valueOf().toString(),\n  },\n  bigint: {\n    from: (bigInt: string | null) => bigInt && parseInt(bigInt, 10),\n    to: (bigInt?: number) => bigInt?.toString(),\n  },\n}\n\n@Entity({ name: \"users\" })\nexport class UserEntity {\n  @PrimaryGeneratedColumn(\"uuid\")\n  id!: string\n\n  @Column({ type: \"varchar\", nullable: true })\n  name!: string | null\n\n  @Column({ type: \"varchar\", nullable: true, unique: true })\n  email!: string | null\n\n  @Column({ type: \"varchar\", nullable: true, transformer: transformer.date })\n  emailVerified!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  image!: string | null\n\n  @OneToMany(() => SessionEntity, (session) => session.userId)\n  sessions!: SessionEntity[]\n\n  @OneToMany(() => AccountEntity, (account) => account.userId)\n  accounts!: AccountEntity[]\n}\n\n@Entity({ name: \"accounts\" })\nexport class AccountEntity {\n  @PrimaryGeneratedColumn(\"uuid\")\n  id!: string\n\n  @Column({ type: \"uuid\" })\n  userId!: string\n\n  @Column()\n  type!: string\n\n  @Column()\n  provider!: string\n\n  @Column()\n  providerAccountId!: string\n\n  @Column({ type: \"varchar\", nullable: true })\n  refresh_token!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  access_token!: string | null\n\n  @Column({\n    nullable: true,\n    type: \"bigint\",\n    transformer: transformer.bigint,\n  })\n  expires_at!: number | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  token_type!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  scope!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  id_token!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  session_state!: string | null\n\n  @ManyToOne(() => UserEntity, (user) => user.accounts, {\n    createForeignKeyConstraints: true,\n  })\n  user!: UserEntity\n}\n\n@Entity({ name: \"sessions\" })\nexport class SessionEntity {\n  @PrimaryGeneratedColumn(\"uuid\")\n  id!: string\n\n  @Column({ unique: true })\n  sessionToken!: string\n\n  @Column({ type: \"uuid\" })\n  userId!: string\n\n  @Column({ transformer: transformer.date })\n  expires!: string\n\n  @ManyToOne(() => UserEntity, (user) => user.sessions)\n  user!: UserEntity\n}\n\n@Entity({ name: \"verification_tokens\" })\nexport class VerificationTokenEntity {\n  @PrimaryGeneratedColumn(\"uuid\")\n  id!: string\n\n  @Column()\n  token!: string\n\n  @Column()\n  identifier!: string\n\n  @Column({ transformer: transformer.date })\n  expires!: string\n}\n"
  },
  {
    "path": "packages/adapter-typeorm/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: \"16px\"}}>\n *  <p>Official <a href=\"https://typeorm.io\">TypeORM</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://typeorm.io\">\n *   <img style={{display: \"block\" }} width=\"56\" src=\"/img/adapters/typeorm.svg\" />\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/typeorm-adapter typeorm\n * ```\n *\n * @module @auth/typeorm-adapter\n */\n\nimport type {\n  Adapter,\n  AdapterUser,\n  AdapterAccount,\n  AdapterSession,\n} from \"@auth/core/adapters\"\nimport { DataSourceOptions, DataSource, EntityManager } from \"typeorm\"\nimport * as defaultEntities from \"./entities.js\"\nimport { parseDataSourceConfig, updateConnectionEntities } from \"./utils.js\"\n\nexport const entities = defaultEntities\n\nexport type Entities = typeof entities\n\n/** This is the interface for the TypeORM adapter options. */\nexport interface TypeORMAdapterOptions {\n  /**\n   * The {@link https://orkhan.gitbook.io/typeorm/docs/entities TypeORM entities} to create the database tables from.\n   */\n  entities?: Entities\n}\n\nlet _dataSource: DataSource | undefined\n\nexport async function getManager(options: {\n  dataSource: string | DataSourceOptions\n  entities: Entities\n}): Promise<EntityManager> {\n  if (!_dataSource) {\n    const { dataSource, entities } = options\n    const config = {\n      ...parseDataSourceConfig(dataSource),\n      entities: Object.values(entities),\n    }\n    _dataSource = new DataSource(config)\n  }\n\n  const manager = _dataSource?.manager\n\n  if (!manager.connection.isInitialized) {\n    await manager.connection.initialize()\n  }\n\n  if (process.env.NODE_ENV !== \"production\") {\n    await updateConnectionEntities(_dataSource, Object.values(options.entities))\n  }\n  return manager\n}\n\nexport function TypeORMAdapter(\n  dataSource: string | DataSourceOptions,\n  options?: TypeORMAdapterOptions\n): Adapter {\n  const entities = options?.entities\n  const c = {\n    dataSource,\n    entities: {\n      UserEntity: entities?.UserEntity ?? defaultEntities.UserEntity,\n      SessionEntity: entities?.SessionEntity ?? defaultEntities.SessionEntity,\n      AccountEntity: entities?.AccountEntity ?? defaultEntities.AccountEntity,\n      VerificationTokenEntity:\n        entities?.VerificationTokenEntity ??\n        defaultEntities.VerificationTokenEntity,\n    },\n  }\n\n  const UserEntityName = c.entities.UserEntity.name\n  const AccountEntityName = c.entities.AccountEntity.name\n  const SessionEntityName = c.entities.SessionEntity.name\n  const VerificationTokenEntityName = c.entities.VerificationTokenEntity.name\n\n  return {\n    /**\n     * Method used in testing. You won't need to call this in your app.\n     * @internal\n     */\n    async __disconnect() {\n      const m = await getManager(c)\n      await m.connection.close()\n    },\n    createUser: async (data) => {\n      const m = await getManager(c)\n      const user = await m.save(UserEntityName, data)\n      return user\n    },\n    // @ts-expect-error\n    async getUser(id) {\n      const m = await getManager(c)\n      const user = await m.findOne(UserEntityName, { where: { id } })\n      if (!user) return null\n      return { ...user }\n    },\n    // @ts-expect-error\n    async getUserByEmail(email) {\n      const m = await getManager(c)\n      const user = await m.findOne(UserEntityName, { where: { email } })\n      if (!user) return null\n      return { ...user }\n    },\n    async getUserByAccount(provider_providerAccountId) {\n      const m = await getManager(c)\n      // @ts-expect-error\n      const account = await m.findOne<AdapterAccount & { user: AdapterUser }>(\n        AccountEntityName,\n        // @ts-expect-error\n        { where: provider_providerAccountId, relations: [\"user\"] }\n      )\n      if (!account) return null\n      return account.user ?? null\n    },\n    // @ts-expect-error\n    async updateUser(data) {\n      const m = await getManager(c)\n      const user = await m.save(UserEntityName, data)\n      return user\n    },\n    async deleteUser(id) {\n      const m = await getManager(c)\n      await m.transaction(async (tm) => {\n        await tm.delete(AccountEntityName, { userId: id })\n        await tm.delete(SessionEntityName, { userId: id })\n        await tm.delete(UserEntityName, { id })\n      })\n    },\n    async linkAccount(data) {\n      const m = await getManager(c)\n      const account = await m.save(AccountEntityName, data)\n      return account\n    },\n    async unlinkAccount(providerAccountId) {\n      const m = await getManager(c)\n      await m.delete<AdapterAccount>(AccountEntityName, providerAccountId)\n    },\n    async createSession(data) {\n      const m = await getManager(c)\n      const session = await m.save(SessionEntityName, data)\n      return session\n    },\n    async getSessionAndUser(sessionToken) {\n      const m = await getManager(c)\n      const sessionAndUser = await m.findOne<\n        AdapterSession & { user: AdapterUser }\n      >(SessionEntityName, { where: { sessionToken }, relations: [\"user\"] })\n\n      if (!sessionAndUser) return null\n      const { user, ...session } = sessionAndUser\n      return { session, user }\n    },\n    async updateSession(data) {\n      const m = await getManager(c)\n      await m.update(\n        SessionEntityName,\n        { sessionToken: data.sessionToken },\n        data\n      )\n      // TODO: Try to return?\n      return null\n    },\n    async deleteSession(sessionToken) {\n      const m = await getManager(c)\n      await m.delete(SessionEntityName, { sessionToken })\n    },\n    async createVerificationToken(data) {\n      const m = await getManager(c)\n      const verificationToken = await m.save(VerificationTokenEntityName, data)\n      // @ts-expect-error\n      delete verificationToken.id\n      return verificationToken\n    },\n    // @ts-expect-error\n    async useVerificationToken(identifier_token) {\n      const m = await getManager(c)\n      const verificationToken = await m.findOne(VerificationTokenEntityName, {\n        where: identifier_token,\n      })\n      if (!verificationToken) {\n        return null\n      }\n      await m.delete(VerificationTokenEntityName, identifier_token)\n      // @ts-expect-error\n      delete verificationToken.id\n      return verificationToken\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-typeorm/src/utils.ts",
    "content": "import type { DataSource, DataSourceOptions } from \"typeorm\"\nimport * as defaultEntities from \"./entities.js\"\n\n/** Ensure configOrString is normalized to an object. */\nexport function parseDataSourceConfig(\n  configOrString: string | DataSourceOptions\n): DataSourceOptions {\n  if (typeof configOrString !== \"string\") {\n    return {\n      ...configOrString,\n      entities: Object.values(configOrString.entities ?? defaultEntities),\n    }\n  }\n\n  // If the input is URL string, automatically convert the string to an object\n  // to make configuration easier (in most use cases).\n  //\n  // TypeORM accepts connection string as a 'url' option, but unfortunately\n  // not for all databases (e.g. SQLite) or for all options, so we handle\n  // parsing it in this function.\n  try {\n    const parsedUrl = new URL(configOrString)\n    const config: any = {\n      entities: Object.values(defaultEntities),\n    }\n\n    if (parsedUrl.protocol.startsWith(\"mongodb+srv\")) {\n      // Special case handling is required for mongodb+srv with TypeORM\n      config.type = \"mongodb\"\n      config.url = configOrString.replace(/\\?(.*)$/, \"\")\n      config.useNewUrlParser = true\n    } else {\n      config.type = parsedUrl.protocol.replace(/:$/, \"\")\n      config.host = parsedUrl.hostname\n      config.port = Number(parsedUrl.port)\n      config.username = parsedUrl.username\n      config.password = parsedUrl.password\n      config.database = parsedUrl.pathname\n        .replace(/^\\//, \"\")\n        .replace(/\\?(.*)$/, \"\")\n      config.options = {}\n    }\n\n    // This option is recommended by mongodb\n    if (config.type === \"mongodb\") {\n      config.useUnifiedTopology = true\n    }\n\n    // Prevents warning about deprecated option (sets default value)\n    if (config.type === \"mssql\") {\n      config.options.enableArithAbort = true\n    }\n\n    if (parsedUrl.search) {\n      parsedUrl.search\n        .replace(/^\\?/, \"\")\n        .split(\"&\")\n        .forEach((keyValuePair) => {\n          let [key, value] = keyValuePair.split(\"=\") as any\n          // Converts true/false strings to actual boolean values\n          if (value === \"true\") {\n            value = true\n          }\n          if (value === \"false\") {\n            value = false\n          }\n          config[key] = value\n        })\n    }\n\n    return config\n  } catch {\n    // If URL parsing fails for any reason, try letting TypeORM handle it\n    return { url: configOrString } as any\n  }\n}\n\nfunction entitiesChanged(\n  prevEntities: any[] | undefined,\n  newEntities: any[]\n): boolean {\n  if (prevEntities?.length !== newEntities?.length) return true\n\n  for (let i = 0; i < prevEntities?.length; i++) {\n    if (prevEntities[i] !== newEntities[i]) return true\n  }\n\n  return false\n}\n\nexport async function updateConnectionEntities(\n  dataSource: DataSource,\n  entities: any[]\n) {\n  if (!entitiesChanged(dataSource.entityMetadatas, entities)) return\n\n  // @ts-expect-error\n  dataSource.entityMetadatas = entities\n\n  // @ts-expect-error\n  await dataSource.buildMetadatas()\n\n  if (dataSource.options.synchronize !== false) {\n    console.warn(\n      \"[next-auth][warn][adapter_typeorm_updating_entities]\",\n      \"\\nhttps://authjs.dev/reference/warnings#adapter_typeorm_updating_entities\"\n    )\n    await dataSource.synchronize()\n  }\n}\n"
  },
  {
    "path": "packages/adapter-typeorm/test/custom-entities.ts",
    "content": "import {\n  Entity,\n  PrimaryGeneratedColumn,\n  Column,\n  ManyToOne,\n  OneToMany,\n  ValueTransformer,\n} from \"typeorm\"\n\nconst transformer: Record<\"date\" | \"bigint\", ValueTransformer> = {\n  date: {\n    from: (date: string | null) => date && new Date(parseInt(date, 10)),\n    to: (date?: Date) => date?.valueOf().toString(),\n  },\n  bigint: {\n    from: (bigInt: string | null) => bigInt && parseInt(bigInt, 10),\n    to: (bigInt?: number) => bigInt?.toString(),\n  },\n}\n\n@Entity({ name: \"users\" })\nexport class UserEntity {\n  @PrimaryGeneratedColumn(\"uuid\")\n  id!: string\n\n  @Column({ type: \"varchar\", nullable: true })\n  name!: string | null\n\n  @Column({ type: \"varchar\", nullable: true, unique: true })\n  email!: string | null\n\n  @Column({ type: \"varchar\", nullable: true, transformer: transformer.date })\n  emailVerified!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  role!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  phone!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  image!: string | null\n\n  @OneToMany(() => SessionEntity, (session) => session.userId)\n  sessions!: SessionEntity[]\n\n  @OneToMany(() => AccountEntity, (account) => account.userId)\n  accounts!: AccountEntity[]\n}\n\n@Entity({ name: \"accounts\" })\nexport class AccountEntity {\n  @PrimaryGeneratedColumn(\"uuid\")\n  id!: string\n\n  @Column({ type: \"uuid\" })\n  userId!: string\n\n  @Column()\n  type!: string\n\n  @Column()\n  provider!: string\n\n  @Column()\n  providerAccountId!: string\n\n  @Column({ type: \"varchar\", nullable: true })\n  refresh_token!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  access_token!: string | null\n\n  @Column({\n    nullable: true,\n    type: \"bigint\",\n    transformer: transformer.bigint,\n  })\n  expires_at!: number | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  token_type!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  scope!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  id_token!: string | null\n\n  @Column({ type: \"varchar\", nullable: true })\n  session_state!: string | null\n\n  @ManyToOne(() => UserEntity, (user) => user.accounts, {\n    createForeignKeyConstraints: true,\n  })\n  user!: UserEntity\n}\n\n@Entity({ name: \"sessions\" })\nexport class SessionEntity {\n  @PrimaryGeneratedColumn(\"uuid\")\n  id!: string\n\n  @Column({ unique: true })\n  sessionToken!: string\n\n  @Column({ type: \"uuid\" })\n  userId!: string\n\n  @Column({ transformer: transformer.date })\n  expires!: string\n\n  @ManyToOne(() => UserEntity, (user) => user.sessions)\n  user!: UserEntity\n}\n\n@Entity({ name: \"verification_tokens\" })\nexport class VerificationTokenEntity {\n  @PrimaryGeneratedColumn(\"uuid\")\n  id!: string\n\n  @Column()\n  token!: string\n\n  @Column()\n  identifier!: string\n\n  @Column({ transformer: transformer.date })\n  expires!: string\n}\n"
  },
  {
    "path": "packages/adapter-typeorm/test/helpers.ts",
    "content": "import { vi } from \"vitest\"\nimport { DataSource } from \"typeorm\"\nimport type { DataSourceOptions } from \"typeorm\"\nimport type { TestOptions } from \"utils/adapter\"\nimport * as defaultEntities from \"../src/entities\"\nimport { parseDataSourceConfig } from \"../src/utils\"\n\nexport { defaultEntities }\n\nconsole.warn = vi.fn()\n\n/** Set up Test Database */\nexport function db(\n  config: string | DataSourceOptions,\n  entities: typeof defaultEntities = defaultEntities\n): TestOptions[\"db\"] {\n  const connection = new DataSource({\n    ...parseDataSourceConfig(config),\n    entities: Object.values(entities),\n  }).manager.connection\n\n  const m = connection.manager\n  return {\n    connect: async () => await connection.initialize(),\n    disconnect: async () => await connection.destroy(),\n    async user(id) {\n      const user = await m.findOne(entities.UserEntity, { where: { id } })\n      return user ?? null\n    },\n    async account(provider_providerAccountId) {\n      const account = await m.findOne(entities.AccountEntity, {\n        where: provider_providerAccountId,\n      })\n      return account ?? null\n    },\n    async session(sessionToken) {\n      const session = await m.findOne(entities.SessionEntity, {\n        where: { sessionToken },\n      })\n      return session ?? null\n    },\n    async verificationToken(token_identifier) {\n      const verificationToken = await m.findOne(\n        entities.VerificationTokenEntity,\n        { where: token_identifier }\n      )\n      if (!verificationToken) return null\n      const { id: _, ...rest } = verificationToken\n      return rest\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-typeorm/test/index.test.ts",
    "content": "import { parseDataSourceConfig } from \"../src/utils\"\n\nconst connectionString = \"mysql://root:password@localhost:3306/next-auth\"\n\ntest(\"could parse connection string\", () => {\n  expect(parseDataSourceConfig(connectionString)).toEqual(\n    expect.objectContaining({\n      type: \"mysql\",\n      host: \"localhost\",\n      port: 3306,\n      username: \"root\",\n      password: \"password\",\n      database: \"next-auth\",\n    })\n  )\n})\n"
  },
  {
    "path": "packages/adapter-typeorm/test/mysql/index.custom.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { TypeORMAdapter } from \"../../src\"\nimport * as entities from \"../custom-entities\"\nimport { db } from \"../helpers\"\nimport { SnakeNamingStrategy } from \"typeorm-naming-strategies\"\n\nimport type { ConnectionOptions } from \"typeorm\"\n\nconst mysqlConfig: ConnectionOptions = {\n  type: \"mysql\" as const,\n  host: \"localhost\",\n  port: 3306,\n  username: \"root\",\n  password: \"password\",\n  database: \"next-auth\",\n  synchronize: true,\n  namingStrategy: new SnakeNamingStrategy(),\n}\n\nrunBasicTests({\n  adapter: TypeORMAdapter(mysqlConfig, {\n    entities,\n  }),\n  db: db(mysqlConfig, entities),\n})\n"
  },
  {
    "path": "packages/adapter-typeorm/test/mysql/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { TypeORMAdapter } from \"../../src\"\nimport { db } from \"../helpers\"\n\nconst mysqlConfig = {\n  type: \"mysql\" as const,\n  host: \"localhost\",\n  port: 3306,\n  username: \"root\",\n  password: \"password\",\n  database: \"next-auth\",\n  synchronize: true,\n}\n\nrunBasicTests({\n  adapter: TypeORMAdapter(mysqlConfig),\n  db: db(mysqlConfig),\n})\n"
  },
  {
    "path": "packages/adapter-typeorm/test/mysql/test.sh",
    "content": "#!/usr/bin/env bash\nset -eu\n\n# Init MySQL container\necho \"Initializing container for MySQL tests\"\n\nMYSQL_DATABASE=next-auth\nMYSQL_ROOT_PASSWORD=password\nMYSQL_CONTAINER_NAME=next-auth-mysql-test\n\nfunction startDatabase {\n  docker run -d --rm \\\n    -e MYSQL_DATABASE=${MYSQL_DATABASE} \\\n    -e MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} \\\n    --name \"${MYSQL_CONTAINER_NAME}\" \\\n    -p 3306:3306 \\\n    mysql:8 --default-authentication-plugin=mysql_native_password\n\n  echo \"Waiting 5s for db to start...\"\n  sleep 5\n}\n\nstartDatabase\necho \"Started running MySQL tests with default models.\"\nif vitest run -c ../utils/vitest.config.ts mysql/index.test.ts; then\n  docker stop ${MYSQL_CONTAINER_NAME}\nelse\n  docker stop ${MYSQL_CONTAINER_NAME} && exit 1\nfi\necho \"Finished running MySQL tests with default models.\"\n\nstartDatabase\necho \"Started running MySQL tests with custom models.\"\nif CUSTOM_MODEL=1 vitest run -c ../utils/vitest.config.ts mysql/index.custom.test.ts; then\n  docker stop ${MYSQL_CONTAINER_NAME}\nelse\n  docker stop ${MYSQL_CONTAINER_NAME} && exit 1\nfi\necho \"Finished running MySQL tests with custom models.\"\n"
  },
  {
    "path": "packages/adapter-typeorm/test/postgresql/index.custom.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { TypeORMAdapter } from \"../../src\"\nimport * as entities from \"../custom-entities\"\nimport { db } from \"../helpers\"\n\nconst postgresConfig =\n  \"postgres://nextauth:password@localhost:5432/nextauth?synchronize=true\"\n\nrunBasicTests({\n  adapter: TypeORMAdapter(postgresConfig, {\n    entities,\n  }),\n  db: db(postgresConfig, entities),\n})\n"
  },
  {
    "path": "packages/adapter-typeorm/test/postgresql/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { TypeORMAdapter } from \"../../src\"\nimport { db } from \"../helpers\"\n\nconst postgresConfig =\n  \"postgres://nextauth:password@localhost:5432/nextauth?synchronize=true\"\n\nrunBasicTests({\n  adapter: TypeORMAdapter(postgresConfig),\n  db: db(postgresConfig),\n})\n"
  },
  {
    "path": "packages/adapter-typeorm/test/postgresql/test.sh",
    "content": "#!/usr/bin/env bash\nset -eu\n\n# Init PostgreSQL container\necho \"Initializing container for PostgreSQL tests\"\n\nPGUSER=nextauth\nPGDATABASE=nextauth\nPG_CONTAINER_NAME=next-auth-postgres-test\n\nfunction startDatabase {\n  docker run -d --rm \\\n    -e POSTGRES_USER=${PGUSER} \\\n    -e POSTGRES_DB=${PGDATABASE} \\\n    -e POSTGRES_HOST_AUTH_METHOD=trust \\\n    --name \"${PG_CONTAINER_NAME}\" \\\n    -p 5432:5432 \\\n    postgres:13.3\n\n  echo \"Waiting 5s for db to start...\"\n  sleep 5\n}\n\nstartDatabase\necho \"Started running PostgreSQL tests with default models.\"\nif vitest run -c ../utils/vitest.config.ts postgresql/index.test.ts; then\n  docker stop ${PG_CONTAINER_NAME}\nelse\n  docker stop ${PG_CONTAINER_NAME} && exit 1\nfi\necho \"Finished running PostgreSQL tests with default models.\"\n\nstartDatabase\necho \"Started running PostgreSQL tests with custom models.\"\nif CUSTOM_MODEL=1 vitest run -c ../utils/vitest.config.ts postgresql/index.custom.test.ts; then\n  docker stop ${PG_CONTAINER_NAME}\nelse\n  docker stop ${PG_CONTAINER_NAME} && exit 1\nfi\necho \"Finished running PostgreSQL tests with custom models.\"\n"
  },
  {
    "path": "packages/adapter-typeorm/test/sqlite/index.custom.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { TypeORMAdapter } from \"../../src\"\nimport * as entities from \"../custom-entities\"\nimport { db } from \"../helpers\"\n\nconst sqliteConfig = {\n  type: \"better-sqlite3\" as const,\n  name: \"next-auth-test-memory\",\n  database: \"./test/sqlite/dev.db\",\n  synchronize: true,\n}\n\nrunBasicTests({\n  adapter: TypeORMAdapter(sqliteConfig, {\n    entities,\n  }),\n  db: db(sqliteConfig, entities),\n})\n"
  },
  {
    "path": "packages/adapter-typeorm/test/sqlite/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { TypeORMAdapter } from \"../../src\"\nimport { db } from \"../helpers\"\nimport { SnakeNamingStrategy } from \"typeorm-naming-strategies\"\n\nimport type { DataSourceOptions } from \"typeorm\"\n\nconst sqliteConfig: DataSourceOptions = {\n  type: \"sqlite\" as const,\n  name: \"next-auth-test-memory\",\n  database: \"./test/sqlite/dev.db\",\n  synchronize: true,\n  namingStrategy: new SnakeNamingStrategy(),\n}\n\nrunBasicTests({\n  adapter: TypeORMAdapter(sqliteConfig),\n  db: db(sqliteConfig),\n})\n"
  },
  {
    "path": "packages/adapter-typeorm/test/sqlite/test.sh",
    "content": "#!/usr/bin/env bash\nset -eu\n\nrm -f test/sqlite/dev.db\n\necho \"Started running SQLite tests with default models.\"\nif vitest run -c ../utils/vitest.config.ts sqlite/index.test.ts; then\n  rm -f db.sqlite\nelse\n  rm -f db.sqlite && exit 1\nfi\necho \"Finished running SQLite tests with default models.\"\n\nrm -f test/sqlite/dev.db\n\necho \"Started running SQLite tests with custom models.\"\nif CUSTOM_MODEL=1 vitest run -c ../utils/vitest.config.ts sqlite/index.custom.test.ts; then\n  rm -f db.sqlite\nelse\n  rm -f db.sqlite && exit 1\nfi\necho \"Finished running SQLite tests with custom models.\"\n\nrm -f test/sqlite/dev.db\n"
  },
  {
    "path": "packages/adapter-typeorm/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-typeorm/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\", \"src/entities.ts\", \"src/utils.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/typeorm-adapter\",\n  entryFileName: \"../typeorm-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-unstorage/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://unstorage.unjs.io/\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/unstorage.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Unstorage Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/unstorage-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/unstorage-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/unstorage-adapter?color=green&label=@auth/unstorage-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/unstorage-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/unstorage-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/unstorage).\n"
  },
  {
    "path": "packages/adapter-unstorage/package.json",
    "content": "{\n  \"name\": \"@auth/unstorage-adapter\",\n  \"version\": \"2.11.0\",\n  \"description\": \"Unstorage adapter for Auth.js.\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"nbifrye <nbifrye@gmail.com>\",\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"unstorage\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"test\": \"./test/test.sh\",\n    \"dev\": \"tsc -w\",\n    \"build\": \"tsc\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"unstorage\": \"^1.10.1\"\n  },\n  \"devDependencies\": {\n    \"redis\": \"^4.6.12\",\n    \"unstorage\": \"^1.10.1\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-unstorage/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: \"16px\"}}>\n *  <p>Official <a href=\"https://unstorage.unjs.io/\">Unstorage</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://unstorage.unjs.io/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/unstorage.svg\" width=\"60\"/>\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install unstorage @auth/unstorage-adapter\n * ```\n *\n * @module @auth/unstorage-adapter\n */\nimport type {\n  Adapter,\n  AdapterUser,\n  AdapterAccount,\n  AdapterSession,\n  AdapterAuthenticator,\n  VerificationToken,\n} from \"@auth/core/adapters\"\nimport { isDate } from \"@auth/core/adapters\"\nimport type { Storage, StorageValue } from \"unstorage\"\n\n/** This is the interface of the Unstorage adapter options. */\nexport interface UnstorageAdapterOptions {\n  /**\n   * The base prefix for your keys\n   */\n  baseKeyPrefix?: string\n  /**\n   * The prefix for the `account` key\n   */\n  accountKeyPrefix?: string\n  /**\n   * The prefix for the `accountByUserId` key\n   */\n  accountByUserIdPrefix?: string\n  /**\n   * The prefix for the `emailKey` key\n   */\n  emailKeyPrefix?: string\n  /**\n   * The prefix for the `sessionKey` key\n   */\n  sessionKeyPrefix?: string\n  /**\n   * The prefix for the `sessionByUserId` key\n   */\n  sessionByUserIdKeyPrefix?: string\n  /**\n   * The prefix for the `user` key\n   */\n  userKeyPrefix?: string\n  /**\n   * The prefix for the `verificationToken` key\n   */\n  verificationTokenKeyPrefix?: string\n  /**\n   * The prefix for the `authenticator` key\n   */\n  authenticatorKeyPrefix?: string\n  /**\n   * The prefix for the `authenticator-by-user-id` key\n   */\n  authenticatorUserKeyPrefix?: string\n  /**\n   * Use `getItemRaw/setItemRaw` instead of `getItem/setItem`.\n   *\n   * This is an experimental feature. Please check [unjs/unstorage#142](https://github.com/unjs/unstorage/issues/142) for more information.\n   */\n  useItemRaw?: boolean\n}\n\nexport const defaultOptions = {\n  baseKeyPrefix: \"\",\n  accountKeyPrefix: \"user:account:\",\n  accountByUserIdPrefix: \"user:account:by-user-id:\",\n  emailKeyPrefix: \"user:email:\",\n  sessionKeyPrefix: \"user:session:\",\n  sessionByUserIdKeyPrefix: \"user:session:by-user-id:\",\n  userKeyPrefix: \"user:\",\n  verificationTokenKeyPrefix: \"user:token:\",\n  authenticatorKeyPrefix: \"authenticator:\",\n  authenticatorUserKeyPrefix: \"authenticator:by-user-id:\",\n  useItemRaw: false,\n}\n\nexport function hydrateDates(json: Record<string, any>) {\n  return Object.entries(json).reduce((acc, [key, val]) => {\n    acc[key] = isDate(val) ? new Date(val as string) : val\n    return acc\n  }, {} as any)\n}\n\nexport function UnstorageAdapter(\n  storage: Storage,\n  options: UnstorageAdapterOptions = {}\n): Adapter {\n  const mergedOptions = {\n    ...defaultOptions,\n    ...options,\n  }\n\n  const { baseKeyPrefix } = mergedOptions\n  const accountKeyPrefix = baseKeyPrefix + mergedOptions.accountKeyPrefix\n  const accountByUserIdPrefix =\n    baseKeyPrefix + mergedOptions.accountByUserIdPrefix\n  const emailKeyPrefix = baseKeyPrefix + mergedOptions.emailKeyPrefix\n  const sessionKeyPrefix = baseKeyPrefix + mergedOptions.sessionKeyPrefix\n  const sessionByUserIdKeyPrefix =\n    baseKeyPrefix + mergedOptions.sessionByUserIdKeyPrefix\n  const userKeyPrefix = baseKeyPrefix + mergedOptions.userKeyPrefix\n  const verificationTokenKeyPrefix =\n    baseKeyPrefix + mergedOptions.verificationTokenKeyPrefix\n  const authenticatorKeyPrefix =\n    baseKeyPrefix + mergedOptions.authenticatorKeyPrefix\n  const authenticatorUserKeyPrefix =\n    baseKeyPrefix + mergedOptions.authenticatorUserKeyPrefix\n\n  async function getItem<T extends StorageValue>(key: string) {\n    if (mergedOptions.useItemRaw) {\n      return await storage.getItemRaw<T>(key)\n    } else {\n      return await storage.getItem<T>(key)\n    }\n  }\n\n  async function setItem(key: string, value: string) {\n    if (mergedOptions.useItemRaw) {\n      return await storage.setItemRaw(key, value)\n    } else {\n      return await storage.setItem(key, value)\n    }\n  }\n\n  const setObjectAsJson = async (key: string, obj: Record<string, any>) => {\n    if (mergedOptions.useItemRaw) {\n      await storage.setItemRaw(key, obj)\n    } else {\n      await storage.setItem(key, JSON.stringify(obj))\n    }\n  }\n\n  const setAccount = async (id: string, account: AdapterAccount) => {\n    const accountKey = accountKeyPrefix + id\n    await Promise.all([\n      setObjectAsJson(accountKey, account),\n      setItem(accountByUserIdPrefix + account.userId, accountKey),\n    ])\n    return account\n  }\n\n  const getAccount = async (id: string) => {\n    const account = await getItem<AdapterAccount>(accountKeyPrefix + id)\n    if (!account) return null\n    return hydrateDates(account)\n  }\n\n  const setSession = async (\n    id: string,\n    session: AdapterSession\n  ): Promise<AdapterSession> => {\n    const sessionKey = sessionKeyPrefix + id\n    await Promise.all([\n      setObjectAsJson(sessionKey, session),\n      setItem(sessionByUserIdKeyPrefix + session.userId, sessionKey),\n    ])\n    return session\n  }\n\n  const getSession = async (id: string) => {\n    const session = await getItem<AdapterSession>(sessionKeyPrefix + id)\n    if (!session) return null\n    return hydrateDates(session)\n  }\n\n  const setUser = async (\n    id: string,\n    user: AdapterUser\n  ): Promise<AdapterUser> => {\n    await Promise.all([\n      setObjectAsJson(userKeyPrefix + id, user),\n      setItem(`${emailKeyPrefix}${user.email as string}`, id),\n    ])\n    return user\n  }\n\n  const getUser = async (id: string) => {\n    const user = await getItem<AdapterUser>(userKeyPrefix + id)\n    if (!user) return null\n    return hydrateDates(user)\n  }\n\n  const setAuthenticator = async (\n    credentialId: string,\n    authenticator: AdapterAuthenticator\n  ): Promise<AdapterAuthenticator> => {\n    const newCredsToSet = [credentialId]\n\n    const getItemReturn = await getItem<string[]>(\n      `${authenticatorUserKeyPrefix}${authenticator.userId}`\n    )\n\n    if (getItemReturn && getItemReturn[0] !== newCredsToSet[0]) {\n      newCredsToSet.push(...getItemReturn)\n    }\n\n    await Promise.all([\n      setObjectAsJson(authenticatorKeyPrefix + credentialId, authenticator),\n      setItem(\n        `${authenticatorUserKeyPrefix}${authenticator.userId}`,\n        JSON.stringify(newCredsToSet)\n      ),\n    ])\n    return authenticator\n  }\n\n  const getAuthenticator = async (credentialId: string) => {\n    const authenticator = await getItem<AdapterAuthenticator>(\n      authenticatorKeyPrefix + credentialId\n    )\n    if (!authenticator) return null\n    return hydrateDates(authenticator)\n  }\n\n  const getAuthenticatorByUserId = async (\n    userId: string\n  ): Promise<AdapterAuthenticator[] | []> => {\n    const credentialIds = await getItem<string[]>(\n      `${authenticatorUserKeyPrefix}${userId}`\n    )\n\n    if (!credentialIds) return []\n\n    const authenticators: AdapterAuthenticator[] = []\n\n    for (const credentialId of credentialIds) {\n      const authenticator = await getAuthenticator(credentialId)\n\n      if (authenticator) {\n        hydrateDates(authenticator)\n        authenticators.push(authenticator)\n      }\n    }\n\n    return authenticators\n  }\n\n  return {\n    async getAccount(providerAccountId: string, provider: string) {\n      const accountId = `${provider}:${providerAccountId}`\n      const account = await getAccount(accountId)\n      if (!account) return null\n      return account\n    },\n    async createUser(user) {\n      const id = crypto.randomUUID()\n      return await setUser(id, { ...user, id })\n    },\n    getUser,\n    async getUserByEmail(email) {\n      const userId = await getItem<string>(emailKeyPrefix + email)\n      if (!userId) {\n        return null\n      }\n      return await getUser(userId)\n    },\n    async getUserByAccount(account) {\n      const dbAccount = await getAccount(\n        `${account.provider}:${account.providerAccountId}`\n      )\n      if (!dbAccount) return null\n      return await getUser(dbAccount.userId)\n    },\n    async updateUser(updates) {\n      const userId = updates.id as string\n      const user = await getUser(userId)\n      return await setUser(userId, { ...(user as AdapterUser), ...updates })\n    },\n    async linkAccount(account) {\n      const id = `${account.provider}:${account.providerAccountId}`\n      return await setAccount(id, { ...account, id })\n    },\n    createSession: (session) => setSession(session.sessionToken, session),\n    async getSessionAndUser(sessionToken) {\n      const session = await getSession(sessionToken)\n      if (!session) return null\n      const user = await getUser(session.userId)\n      if (!user) return null\n      return { session, user }\n    },\n    async updateSession(updates) {\n      const session = await getSession(updates.sessionToken)\n      if (!session) return null\n      return await setSession(updates.sessionToken, { ...session, ...updates })\n    },\n    async deleteSession(sessionToken) {\n      await storage.removeItem(sessionKeyPrefix + sessionToken)\n    },\n    async createVerificationToken(verificationToken) {\n      await setObjectAsJson(\n        verificationTokenKeyPrefix +\n          verificationToken.identifier +\n          \":\" +\n          verificationToken.token,\n        verificationToken\n      )\n      return verificationToken\n    },\n    async useVerificationToken(verificationToken) {\n      const tokenKey =\n        verificationTokenKeyPrefix +\n        verificationToken.identifier +\n        \":\" +\n        verificationToken.token\n\n      const token = await getItem<VerificationToken>(tokenKey)\n      if (!token) return null\n\n      await storage.removeItem(tokenKey)\n      return hydrateDates(token)\n    },\n    async unlinkAccount(account) {\n      const id = `${account.provider}:${account.providerAccountId}`\n      const dbAccount = await getAccount(id)\n      if (!dbAccount) return\n      const accountKey = `${accountKeyPrefix}${id}`\n      await Promise.all([\n        storage.removeItem(accountKey),\n        storage.removeItem(\n          `${accountByUserIdPrefix} + ${dbAccount.userId as string}`\n        ),\n      ])\n    },\n    async deleteUser(userId) {\n      const user = await getUser(userId)\n      if (!user) return\n      const accountByUserKey = accountByUserIdPrefix + userId\n      const accountKey = await getItem<string>(accountByUserKey)\n      const sessionByUserIdKey = sessionByUserIdKeyPrefix + userId\n      const sessionKey = await getItem<string>(sessionByUserIdKey)\n      await Promise.all([\n        storage.removeItem(userKeyPrefix + userId),\n        storage.removeItem(`${emailKeyPrefix}${user.email as string}`),\n        storage.removeItem(accountKey as string),\n        storage.removeItem(accountByUserKey),\n        storage.removeItem(sessionKey as string),\n        storage.removeItem(sessionByUserIdKey),\n      ])\n    },\n    async createAuthenticator(authenticator) {\n      await setAuthenticator(authenticator.credentialID, authenticator)\n      return authenticator\n    },\n    async getAuthenticator(credentialID) {\n      return getAuthenticator(credentialID)\n    },\n    async listAuthenticatorsByUserId(userId) {\n      const user = await getUser(userId)\n      if (!user) return []\n      return getAuthenticatorByUserId(user.id)\n    },\n    async updateAuthenticatorCounter(credentialID, counter) {\n      const authenticator = await getAuthenticator(credentialID)\n      authenticator.counter = Number(counter)\n      await setAuthenticator(credentialID, authenticator)\n      return authenticator\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-unstorage/test/filesystem.test.ts",
    "content": "import { createStorage } from \"unstorage\"\nimport { runBasicTests } from \"utils/adapter\"\nimport { hydrateDates, UnstorageAdapter } from \"../src\"\nimport fsDriver from \"unstorage/drivers/fs\"\n\nconst storage = createStorage({\n  driver: fsDriver({ base: \"./tmp\" }),\n})\n\nrunBasicTests({\n  adapter: UnstorageAdapter(storage, { baseKeyPrefix: \"testApp:\" }),\n  testWebAuthnMethods: true,\n  db: {\n    disconnect: storage.dispose,\n    async user(id: string) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:user:${id}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async account({ provider, providerAccountId }) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:user:account:${provider}:${providerAccountId}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async session(sessionToken) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:user:session:${sessionToken}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async verificationToken(where) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:user:token:${where.identifier}:${where.token}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async authenticator(id) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:authenticator:${id}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-unstorage/test/memory.test.ts",
    "content": "import { createStorage } from \"unstorage\"\nimport { runBasicTests } from \"utils/adapter\"\nimport { hydrateDates, UnstorageAdapter } from \"../src\"\n\nconst storage = createStorage()\n\nrunBasicTests({\n  adapter: UnstorageAdapter(storage, { baseKeyPrefix: \"testApp:\" }),\n  testWebAuthnMethods: true,\n  db: {\n    disconnect: storage.dispose,\n    async user(id: string) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:user:${id}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async account({ provider, providerAccountId }) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:user:account:${provider}:${providerAccountId}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async session(sessionToken) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:user:session:${sessionToken}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async verificationToken(where) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:user:token:${where.identifier}:${where.token}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async authenticator(id) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:authenticator:${id}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-unstorage/test/redis-json.test.ts",
    "content": "import { createStorage } from \"unstorage\"\nimport { runBasicTests } from \"utils/adapter\"\nimport { hydrateDates, UnstorageAdapter } from \"../src\"\nimport { defineDriver } from \"unstorage\"\nimport { createClient } from \"redis\"\nimport type { RedisClientType, RedisClientOptions } from \"redis\"\n\n// TODO: These tests just hang and time out so currently we are skipping them\nconst redisJSONDriver = defineDriver((options: RedisClientOptions) => {\n  let redisClient: RedisClientType\n  const getRedisClient = async () => {\n    if (!redisClient) {\n      // @ts-ignore\n      redisClient = await createClient(options).connect()\n      return redisClient\n    }\n    if (!redisClient.isReady) await redisClient.connect()\n    return redisClient\n  }\n\n  return {\n    name: \"redis-json-driver\",\n    options,\n    async hasItem(key, _opts) {\n      return Boolean(await (await getRedisClient()).exists(key))\n    },\n    async getItem(key, _opts) {\n      const value = await (await getRedisClient()).get(key)\n      return value ?? null\n    },\n    async getItemRaw(key, _opts) {\n      const value = await (await getRedisClient()).json.get(key)\n      return value ?? null\n    },\n    async setItem(key, value, _opts) {\n      let ttl = _opts?.ttl\n      if (ttl) {\n        await (await getRedisClient()).set(key, value)\n        await (await getRedisClient()).expire(key, ttl)\n      } else {\n        await (await getRedisClient()).set(key, value)\n      }\n    },\n    async setItemRaw(key, value, _opts) {\n      let ttl = _opts?.ttl\n      await (await getRedisClient()).json.set(key, \"$\", value)\n      if (ttl) await (await getRedisClient()).expire(key, ttl)\n    },\n    async removeItem(key, _opts) {\n      await (await getRedisClient()).del(key)\n    },\n    async getKeys(base, _opts) {\n      return await (await getRedisClient()).keys(\"*\")\n    },\n  }\n})\n\nconst storage = createStorage({\n  driver: redisJSONDriver({\n    username: \"default\",\n  }),\n})\n\nrunBasicTests({\n  adapter: UnstorageAdapter(storage, {\n    baseKeyPrefix: \"testApp:\",\n    useItemRaw: true,\n  }),\n  // TODO: Unstorage `*Raw` methods not working as expected\n  testWebAuthnMethods: false,\n  db: {\n    disconnect: storage.dispose,\n    async user(id: string) {\n      const data = await storage.getItemRaw<Record<string, unknown>>(\n        `testApp:user:${id}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async account({ provider, providerAccountId }) {\n      const data = await storage.getItemRaw<Record<string, unknown>>(\n        `testApp:user:account:${provider}:${providerAccountId}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async session(sessionToken) {\n      const data = await storage.getItemRaw<Record<string, unknown>>(\n        `testApp:user:session:${sessionToken}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async verificationToken(where) {\n      const data = await storage.getItemRaw<Record<string, unknown>>(\n        `testApp:user:token:${where.identifier}:${where.token}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async authenticator(id) {\n      const data = await storage.getItemRaw<Record<string, unknown>>(\n        `testApp:authenticator:${id}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-unstorage/test/redis.test.ts",
    "content": "import { createStorage } from \"unstorage\"\nimport { runBasicTests } from \"utils/adapter\"\nimport { hydrateDates, UnstorageAdapter } from \"../src\"\nimport redisDriver from \"unstorage/drivers/redis\"\n\nconst storage = createStorage({\n  driver: redisDriver({\n    username: \"default\",\n  }),\n})\n\nrunBasicTests({\n  adapter: UnstorageAdapter(storage, { baseKeyPrefix: \"testApp:\" }),\n  testWebAuthnMethods: true,\n  db: {\n    disconnect: storage.dispose,\n    async user(id: string) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:user:${id}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async account({ provider, providerAccountId }) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:user:account:${provider}:${providerAccountId}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async session(sessionToken) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:user:session:${sessionToken}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async verificationToken(where) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:user:token:${where.identifier}:${where.token}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async authenticator(id) {\n      const data = await storage.getItem<Record<string, unknown>>(\n        `testApp:authenticator:${id}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-unstorage/test/test.sh",
    "content": "#!/usr/bin/env bash\n\nCONTAINER_NAME=authjs-unstorage-test\n\n# Start db\ndocker run -d --rm \\\n  -p 6379:6379 \\\n  --name ${CONTAINER_NAME} \\\n  redis/redis-stack:7.2.0-v10\n\nsleep 10\n\n# Always stop container, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  docker stop ${CONTAINER_NAME}\nelse\n  docker stop ${CONTAINER_NAME} && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-unstorage/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-unstorage/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/unstorage-adapter\",\n  entryFileName: \"../unstorage-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-upstash-redis/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://docs.upstash.com/redis\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/upstash-redis.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Upstash Redis Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/upstash-redis-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/upstash-redis-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/upstash-redis-adapter?color=green&label=@auth/upstash-redis-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/upstash-redis-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/upstash-redis-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/upstash-redis).\n"
  },
  {
    "path": "packages/adapter-upstash-redis/docker-compose.yml",
    "content": "services:\n  redis:\n    image: redis\n    ports:\n      - \"6379:6379\"\n  serverless-redis-http:\n    ports:\n      - \"8079:80\"\n    image: hiett/serverless-redis-http:latest\n    environment:\n      SRH_MODE: env\n      SRH_TOKEN: uwndz1YIfm9k78mx+mjW8qe7CX33VxRYnscDpZVkt4Y=\n      SRH_CONNECTION_STRING: \"redis://redis:6379\"\n"
  },
  {
    "path": "packages/adapter-upstash-redis/package.json",
    "content": "{\n  \"name\": \"@auth/upstash-redis-adapter\",\n  \"version\": \"2.11.0\",\n  \"description\": \"Upstash adapter for Auth.js.\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"github.com/kay-is\",\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"upstash\",\n    \"redis\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"test\": \"./test/test.sh\",\n    \"build\": \"tsc\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"@upstash/redis\": \"^1.0.1\"\n  },\n  \"devDependencies\": {\n    \"@types/uuid\": \"^8.3.3\",\n    \"@upstash/redis\": \"^1.0.1\",\n    \"dotenv\": \"^10.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-upstash-redis/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>Official <a href=\"https://docs.upstash.com/redis\">Upstash Redis</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://docs.upstash.com/redis\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/upstash-redis.svg\" width=\"60\"/>\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @upstash/redis @auth/upstash-redis-adapter\n * ```\n *\n * @module @auth/upstash-redis-adapter\n */\nimport {\n  type Adapter,\n  type AdapterUser,\n  type AdapterAccount,\n  type AdapterSession,\n  type VerificationToken,\n  isDate,\n} from \"@auth/core/adapters\"\nimport type { Redis } from \"@upstash/redis\"\n\n/** This is the interface of the Upstash Redis adapter options. */\nexport interface UpstashRedisAdapterOptions {\n  /**\n   * The base prefix for your keys\n   */\n  baseKeyPrefix?: string\n  /**\n   * The prefix for the `account` key\n   */\n  accountKeyPrefix?: string\n  /**\n   * The prefix for the `accountByUserId` key\n   */\n  accountByUserIdPrefix?: string\n  /**\n   * The prefix for the `emailKey` key\n   */\n  emailKeyPrefix?: string\n  /**\n   * The prefix for the `sessionKey` key\n   */\n  sessionKeyPrefix?: string\n  /**\n   * The prefix for the `sessionByUserId` key\n   */\n  sessionByUserIdKeyPrefix?: string\n  /**\n   * The prefix for the `user` key\n   */\n  userKeyPrefix?: string\n  /**\n   * The prefix for the `verificationToken` key\n   */\n  verificationTokenKeyPrefix?: string\n}\n\nexport const defaultOptions = {\n  baseKeyPrefix: \"\",\n  accountKeyPrefix: \"user:account:\",\n  accountByUserIdPrefix: \"user:account:by-user-id:\",\n  emailKeyPrefix: \"user:email:\",\n  sessionKeyPrefix: \"user:session:\",\n  sessionByUserIdKeyPrefix: \"user:session:by-user-id:\",\n  userKeyPrefix: \"user:\",\n  verificationTokenKeyPrefix: \"user:token:\",\n}\n\nexport function hydrateDates(json: object) {\n  return Object.entries(json).reduce((acc, [key, val]) => {\n    acc[key] = isDate(val) ? new Date(val as string) : val\n    return acc\n  }, {} as any)\n}\n\nexport function UpstashRedisAdapter(\n  client: Redis,\n  options: UpstashRedisAdapterOptions = {}\n): Adapter {\n  const mergedOptions = {\n    ...defaultOptions,\n    ...options,\n  }\n\n  const { baseKeyPrefix } = mergedOptions\n  const accountKeyPrefix = baseKeyPrefix + mergedOptions.accountKeyPrefix\n  const accountByUserIdPrefix =\n    baseKeyPrefix + mergedOptions.accountByUserIdPrefix\n  const emailKeyPrefix = baseKeyPrefix + mergedOptions.emailKeyPrefix\n  const sessionKeyPrefix = baseKeyPrefix + mergedOptions.sessionKeyPrefix\n  const sessionByUserIdKeyPrefix =\n    baseKeyPrefix + mergedOptions.sessionByUserIdKeyPrefix\n  const userKeyPrefix = baseKeyPrefix + mergedOptions.userKeyPrefix\n  const verificationTokenKeyPrefix =\n    baseKeyPrefix + mergedOptions.verificationTokenKeyPrefix\n\n  const setObjectAsJson = async (key: string, obj: any) =>\n    await client.set(key, JSON.stringify(obj))\n\n  const setAccount = async (id: string, account: AdapterAccount) => {\n    const accountKey = accountKeyPrefix + id\n    await setObjectAsJson(accountKey, account)\n    await client.set(accountByUserIdPrefix + account.userId, accountKey)\n    return account\n  }\n\n  const getAccount = async (id: string) => {\n    const account = await client.get<AdapterAccount>(accountKeyPrefix + id)\n    if (!account) return null\n    return hydrateDates(account)\n  }\n\n  const setSession = async (\n    id: string,\n    session: AdapterSession\n  ): Promise<AdapterSession> => {\n    const sessionKey = sessionKeyPrefix + id\n    await setObjectAsJson(sessionKey, session)\n    await client.set(sessionByUserIdKeyPrefix + session.userId, sessionKey)\n    return session\n  }\n\n  const getSession = async (id: string) => {\n    const session = await client.get<AdapterSession>(sessionKeyPrefix + id)\n    if (!session) return null\n    return hydrateDates(session)\n  }\n\n  const setUser = async (\n    id: string,\n    user: AdapterUser\n  ): Promise<AdapterUser> => {\n    await setObjectAsJson(userKeyPrefix + id, user)\n    await client.set(`${emailKeyPrefix}${user.email as string}`, id)\n    return user\n  }\n\n  const getUser = async (id: string) => {\n    const user = await client.get<AdapterUser>(userKeyPrefix + id)\n    if (!user) return null\n    return hydrateDates(user)\n  }\n\n  return {\n    async createUser(user) {\n      const id = crypto.randomUUID()\n      // TypeScript thinks the emailVerified field is missing\n      // but all fields are copied directly from user, so it's there\n      return await setUser(id, { ...user, id })\n    },\n    getUser,\n    async getUserByEmail(email) {\n      const userId = await client.get<string>(emailKeyPrefix + email)\n      if (!userId) {\n        return null\n      }\n      return await getUser(userId)\n    },\n    async getUserByAccount(account) {\n      const dbAccount = await getAccount(\n        `${account.provider}:${account.providerAccountId}`\n      )\n      if (!dbAccount) return null\n      return await getUser(dbAccount.userId)\n    },\n    async updateUser(updates) {\n      const userId = updates.id as string\n      const user = await getUser(userId)\n      return await setUser(userId, { ...(user as AdapterUser), ...updates })\n    },\n    async linkAccount(account) {\n      const id = `${account.provider}:${account.providerAccountId}`\n      return await setAccount(id, { ...account, id })\n    },\n    createSession: (session) => setSession(session.sessionToken, session),\n    async getSessionAndUser(sessionToken) {\n      const session = await getSession(sessionToken)\n      if (!session) return null\n      const user = await getUser(session.userId)\n      if (!user) return null\n      return { session, user }\n    },\n    async updateSession(updates) {\n      const session = await getSession(updates.sessionToken)\n      if (!session) return null\n      return await setSession(updates.sessionToken, { ...session, ...updates })\n    },\n    async deleteSession(sessionToken) {\n      await client.del(sessionKeyPrefix + sessionToken)\n    },\n    async createVerificationToken(verificationToken) {\n      await setObjectAsJson(\n        verificationTokenKeyPrefix +\n          verificationToken.identifier +\n          \":\" +\n          verificationToken.token,\n        verificationToken\n      )\n      return verificationToken\n    },\n    async useVerificationToken(verificationToken) {\n      const tokenKey =\n        verificationTokenKeyPrefix +\n        verificationToken.identifier +\n        \":\" +\n        verificationToken.token\n\n      const token = await client.get<VerificationToken>(tokenKey)\n      if (!token) return null\n\n      await client.del(tokenKey)\n      return hydrateDates(token)\n      // return reviveFromJson(token)\n    },\n    async unlinkAccount(account) {\n      const id = `${account.provider}:${account.providerAccountId}`\n      const dbAccount = await getAccount(id)\n      if (!dbAccount) return\n      const accountKey = `${accountKeyPrefix}${id}`\n      await client.del(\n        accountKey,\n        `${accountByUserIdPrefix} + ${dbAccount.userId as string}`\n      )\n    },\n    async deleteUser(userId) {\n      const user = await getUser(userId)\n      if (!user) return\n      const accountByUserKey = accountByUserIdPrefix + userId\n      const accountKey = await client.get<string>(accountByUserKey)\n      const sessionByUserIdKey = sessionByUserIdKeyPrefix + userId\n      const sessionKey = await client.get<string>(sessionByUserIdKey)\n      await client.del(\n        userKeyPrefix + userId,\n        `${emailKeyPrefix}${user.email as string}`,\n        accountKey as string,\n        accountByUserKey,\n        sessionKey as string,\n        sessionByUserIdKey\n      )\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-upstash-redis/test/index.test.ts",
    "content": "import { Redis } from \"@upstash/redis\"\nimport { runBasicTests } from \"utils/adapter\"\nimport { hydrateDates, UpstashRedisAdapter } from \"../src\"\nimport \"dotenv/config\"\n\nconst client = new Redis({\n  url: \"http://localhost:8079\",\n  token: \"uwndz1YIfm9k78mx+mjW8qe7CX33VxRYnscDpZVkt4Y=\",\n})\n\nrunBasicTests({\n  adapter: UpstashRedisAdapter(client, { baseKeyPrefix: \"testApp:\" }),\n  db: {\n    disconnect: client.flushdb,\n    async user(id: string) {\n      const data = await client.get<object>(`testApp:user:${id}`)\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async account({ provider, providerAccountId }) {\n      const data = await client.get<object>(\n        `testApp:user:account:${provider}:${providerAccountId}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async session(sessionToken) {\n      const data = await client.get<object>(\n        `testApp:user:session:${sessionToken}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n    async verificationToken(where) {\n      const data = await client.get<object>(\n        `testApp:user:token:${where.identifier}:${where.token}`\n      )\n      if (!data) return null\n      return hydrateDates(data)\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-upstash-redis/test/test.sh",
    "content": "#!/usr/bin/env bash\n\necho \"Initializing container for Upstash-Redis tests (Serverless-Redis-HTTP)\"\n\n# Init Redis + serverless-redis-http containers\ndocker compose up -d\n\necho \"Waiting 5s for db to start...\"\nsleep 5\n\n# Always stop container, but exit with 1 when tests are failing\nif vitest run -c ../utils/vitest.config.ts; then\n  docker compose down -v\nelse\n  docker compose down -v && exit 1\nfi\n"
  },
  {
    "path": "packages/adapter-upstash-redis/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-upstash-redis/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/upstash-redis-adapter\",\n  entryFileName: \"../upstash-redis-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/adapter-xata/README.md",
    "content": "<p align=\"center\">\n  <br/>\n  <a href=\"https://authjs.dev\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/logo-sm.png\" />\n  </a>\n  <a href=\"https://xata.io\" target=\"_blank\">\n    <img height=\"64px\" src=\"https://authjs.dev/img/adapters/xata.svg\"/>\n  </a>\n  <h3 align=\"center\"><b>Xata Adapter</b> - NextAuth.js / Auth.js</a></h3>\n  <p align=\"center\" style=\"align: center;\">\n    <a href=\"https://npm.im/@auth/xata-adapter\">\n      <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n    </a>\n    <a href=\"https://npm.im/@auth/xata-adapter\">\n      <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/xata-adapter?color=green&label=@auth/xata-adapter&style=flat-square\">\n    </a>\n    <a href=\"https://www.npmtrends.com/@auth/xata-adapter\">\n      <img src=\"https://img.shields.io/npm/dm/@auth/xata-adapter?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n    </a>\n    <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n      <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n    </a>\n  </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/xata).\n"
  },
  {
    "path": "packages/adapter-xata/package.json",
    "content": "{\n  \"name\": \"@auth/xata-adapter\",\n  \"version\": \"1.11.0\",\n  \"description\": \"Xata adapter for Auth.js\",\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nextauthjs/next-auth/issues\"\n  },\n  \"author\": \"Tejas Kumar\",\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"keywords\": [\n    \"next-auth\",\n    \"next.js\",\n    \"oauth\",\n    \"xata\"\n  ],\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"tsc\",\n    \"test\": \"vitest -c ../utils/vitest.config.ts\",\n    \"clean\": \"rm -rf *.js *.d.ts*\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"@xata.io/client\": \">=0.28.0\"\n  },\n  \"devDependencies\": {\n    \"@xata.io/client\": \"^0.28.0\"\n  }\n}\n"
  },
  {
    "path": "packages/adapter-xata/schema.json",
    "content": "{\n  \"tables\": [\n    {\n      \"name\": \"authjs_users\",\n      \"columns\": [\n        {\n          \"name\": \"email\",\n          \"type\": \"email\"\n        },\n        {\n          \"name\": \"emailVerified\",\n          \"type\": \"datetime\"\n        },\n        {\n          \"name\": \"name\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"image\",\n          \"type\": \"string\"\n        }\n      ]\n    },\n    {\n      \"name\": \"authjs_accounts\",\n      \"columns\": [\n        {\n          \"name\": \"user\",\n          \"type\": \"link\",\n          \"link\": {\n            \"table\": \"authjs_users\"\n          }\n        },\n        {\n          \"name\": \"type\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"provider\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"providerAccountId\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"refresh_token\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"access_token\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"expires_at\",\n          \"type\": \"int\"\n        },\n        {\n          \"name\": \"token_type\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"scope\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"id_token\",\n          \"type\": \"text\"\n        },\n        {\n          \"name\": \"session_state\",\n          \"type\": \"string\"\n        }\n      ]\n    },\n    {\n      \"name\": \"authjs_verificationTokens\",\n      \"columns\": [\n        {\n          \"name\": \"identifier\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"token\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"expires\",\n          \"type\": \"datetime\"\n        }\n      ]\n    },\n    {\n      \"name\": \"authjs_users_accounts\",\n      \"columns\": [\n        {\n          \"name\": \"user\",\n          \"type\": \"link\",\n          \"link\": {\n            \"table\": \"authjs_users\"\n          }\n        },\n        {\n          \"name\": \"account\",\n          \"type\": \"link\",\n          \"link\": {\n            \"table\": \"authjs_accounts\"\n          }\n        }\n      ]\n    },\n    {\n      \"name\": \"authjs_users_sessions\",\n      \"columns\": [\n        {\n          \"name\": \"user\",\n          \"type\": \"link\",\n          \"link\": {\n            \"table\": \"authjs_users\"\n          }\n        },\n        {\n          \"name\": \"session\",\n          \"type\": \"link\",\n          \"link\": {\n            \"table\": \"authjs_sessions\"\n          }\n        }\n      ]\n    },\n    {\n      \"name\": \"authjs_sessions\",\n      \"columns\": [\n        {\n          \"name\": \"sessionToken\",\n          \"type\": \"string\"\n        },\n        {\n          \"name\": \"expires\",\n          \"type\": \"datetime\"\n        },\n        {\n          \"name\": \"user\",\n          \"type\": \"link\",\n          \"link\": {\n            \"table\": \"authjs_users\"\n          }\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/adapter-xata/src/index.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: 16}}>\n *  <p>Official <a href=\"https://xata.io/docs/overview\">Xata</a> adapter for Auth.js / NextAuth.js.</p>\n *  <a href=\"https://xata.io/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/adapters/xata.svg\" width=\"50\"/>\n *  </a>\n * </div>\n *\n * ## Installation\n *\n * 1. Install Auth.js and the Xata adapter\n *\n * ```bash npm2yarn\n * npm install @auth/xata-adapter\n * ```\n *\n * 2. Install the Xata CLI globally if you don't already have it\n *\n * ```bash npm2yarn\n * npm install -g @xata.io/cli\n * ```\n *\n * 3. Login\n *\n * ```bash\n * xata auth login\n * ```\n *\n * @module @auth/xata-adapter\n */\n\nimport type { Adapter } from \"@auth/core/adapters\"\nimport type { XataClient } from \"./xata.js\"\n\nexport function XataAdapter(client: XataClient): Adapter {\n  return {\n    async createUser(user) {\n      const newUser = await client.db.nextauth_users.create(user)\n      return newUser\n    },\n    async getUser(id) {\n      const user = await client.db.nextauth_users.filter({ id }).getFirst()\n      return user ?? null\n    },\n    async getUserByEmail(email) {\n      const user = await client.db.nextauth_users.filter({ email }).getFirst()\n      return user ?? null\n    },\n    async getUserByAccount({ providerAccountId, provider }) {\n      const result = await client.db.nextauth_users_accounts\n        .select([\"user.*\"])\n        .filter({\n          \"account.providerAccountId\": providerAccountId,\n          \"account.provider\": provider,\n        })\n        .getFirst()\n      const user = result?.user\n      return user ?? null\n    },\n    async updateUser(user) {\n      const result = await client.db.nextauth_users.update(user.id!, user)\n      return (\n        result ?? {\n          ...user,\n          id: user.id!,\n          emailVerified: user.emailVerified ?? null,\n        }\n      )\n    },\n    async deleteUser(id) {\n      return await client.db.nextauth_users.delete(id)\n    },\n    async linkAccount(initialAccount) {\n      const { userId, ...account } = initialAccount\n      const newXataAccount = await client.db.nextauth_accounts.create({\n        ...account,\n        user: { id: userId },\n      })\n      await client.db.nextauth_users_accounts.create({\n        user: { id: userId },\n        account: { id: newXataAccount.id },\n      })\n    },\n    async unlinkAccount({ providerAccountId, provider }) {\n      /**\n       * @todo refactor this when we support DELETE WHERE.\n       */\n      const connectedAccount = await client.db.nextauth_users_accounts\n        .filter({\n          \"account.providerAccountId\": providerAccountId,\n          \"account.provider\": provider,\n        })\n        .getFirst()\n\n      if (!connectedAccount) {\n        return\n      }\n      return await client.db.nextauth_users_accounts.delete(connectedAccount.id)\n    },\n    async createSession(initialSession) {\n      const { userId, ...session } = initialSession\n      const newXataSession = await client.db.nextauth_sessions.create({\n        ...session,\n        user: { id: userId },\n      })\n      await client.db.nextauth_users_sessions.create({\n        user: { id: userId },\n        session: { id: newXataSession.id },\n      })\n      return { ...session, ...newXataSession, userId }\n    },\n    async getSessionAndUser(sessionToken) {\n      const result = await client.db.nextauth_users_sessions\n        .select([\"user.*\", \"session.*\"])\n        .filter({ \"session.sessionToken\": sessionToken })\n        .getFirst()\n      if (!result?.session || !result?.user) {\n        return null\n      }\n\n      return {\n        session: {\n          ...result.session,\n          sessionToken: result.session.sessionToken!,\n          expires: result.session.expires!,\n          userId: result.user.id,\n        },\n        user: {\n          ...result.user,\n          emailVerified: result.user.emailVerified ?? null,\n        },\n      }\n    },\n    async updateSession({ sessionToken, ...data }) {\n      const session = await client.db.nextauth_sessions\n        .filter({ sessionToken })\n        .getFirst()\n      if (!session) {\n        return null\n      }\n\n      await client.db.nextauth_sessions.update({ ...session, ...data })\n      return {\n        ...session,\n        sessionToken,\n        userId: data.userId!,\n        expires: data.expires!,\n      }\n    },\n\n    async deleteSession(sessionToken) {\n      /**\n       * @todo refactor this when we support DELETE WHERE.\n       */\n      const session = await client.db.nextauth_sessions\n        .filter({ sessionToken })\n        .getFirst()\n      if (!session) {\n        return\n      }\n      const connectedSession = await client.db.nextauth_users_sessions\n        .filter({ \"session.sessionToken\": sessionToken })\n        .getFirst()\n      if (!connectedSession) {\n        return\n      }\n      await client.db.nextauth_sessions.delete(session.id)\n      await client.db.nextauth_users_sessions.delete(connectedSession.id)\n    },\n    async createVerificationToken(token) {\n      await client.db.nextauth_verificationTokens.create({\n        expires: token.expires,\n        identifier: token.identifier,\n        token: token.token,\n      })\n      return token\n    },\n    async useVerificationToken(token) {\n      /**\n       * @todo refactor this when we support DELETE WHERE.\n       */\n      const xataToken = await client.db.nextauth_verificationTokens\n        .filter({ identifier: token.identifier, token: token.token })\n        .getFirst()\n      if (!xataToken) {\n        return null\n      }\n      await client.db.nextauth_verificationTokens.delete(xataToken.id)\n      return { ...token, expires: new Date() }\n    },\n  }\n}\n"
  },
  {
    "path": "packages/adapter-xata/src/xata.ts",
    "content": "/**\n * This file is auto-generated from Xata and corresponds\n * to the database types in the Xata database. Please do not\n * augment by hand.\n */\nimport {\n  buildClient,\n  BaseClientOptions,\n  XataRecord,\n  ClientConstructor,\n  type BaseSchema,\n} from \"@xata.io/client\"\n\nexport interface NextauthUser {\n  email?: string | null\n  emailVerified?: Date | null\n  name?: string | null\n  image?: string | null\n}\n\nexport type NextauthUserRecord = NextauthUser & XataRecord\n\nexport interface NextauthAccount {\n  user?: NextauthUserRecord | null\n  type?: string | null\n  provider?: string | null\n  providerAccountId?: string | null\n  refresh_token?: string | null\n  access_token?: string | null\n  expires_at?: number | null\n  token_type?: string | null\n  scope?: string | null\n  id_token?: string | null\n  session_state?: string | null\n}\n\nexport type NextauthAccountRecord = NextauthAccount & XataRecord\n\nexport interface NextauthVerificationToken {\n  identifier?: string | null\n  token?: string | null\n  expires?: Date | null\n}\n\nexport type NextauthVerificationTokenRecord = NextauthVerificationToken &\n  XataRecord\n\nexport interface NextauthUsersAccount {\n  user?: NextauthUserRecord | null\n  account?: NextauthAccountRecord | null\n}\n\nexport type NextauthUsersAccountRecord = NextauthUsersAccount & XataRecord\n\nexport interface NextauthUsersSession {\n  user?: NextauthUserRecord | null\n  session?: NextauthSessionRecord | null\n}\n\nexport type NextauthUsersSessionRecord = NextauthUsersSession & XataRecord\n\nexport interface NextauthSession {\n  sessionToken?: string | null\n  expires?: Date | null\n  user?: NextauthUserRecord | null\n}\n\nexport type NextauthSessionRecord = NextauthSession & XataRecord\n\nexport type DatabaseSchema = {\n  nextauth_users: NextauthUserRecord\n  nextauth_accounts: NextauthAccountRecord\n  nextauth_verificationTokens: NextauthVerificationTokenRecord\n  nextauth_users_accounts: NextauthUsersAccountRecord\n  nextauth_users_sessions: NextauthUsersSessionRecord\n  nextauth_sessions: NextauthSessionRecord\n}\n\nconst schemas: BaseSchema[] = [\n  {\n    name: \"nextauth_users\",\n    columns: [\n      {\n        name: \"email\",\n        type: \"email\",\n      },\n      {\n        name: \"emailVerified\",\n        type: \"datetime\",\n      },\n      {\n        name: \"name\",\n        type: \"string\",\n      },\n      {\n        name: \"image\",\n        type: \"string\",\n      },\n    ],\n  },\n  {\n    name: \"nextauth_accounts\",\n    columns: [\n      {\n        name: \"user\",\n        type: \"link\",\n        link: {\n          table: \"nextauth_users\",\n        },\n      },\n      {\n        name: \"type\",\n        type: \"string\",\n      },\n      {\n        name: \"provider\",\n        type: \"string\",\n      },\n      {\n        name: \"providerAccountId\",\n        type: \"string\",\n      },\n      {\n        name: \"refresh_token\",\n        type: \"string\",\n      },\n      {\n        name: \"access_token\",\n        type: \"string\",\n      },\n      {\n        name: \"expires_at\",\n        type: \"int\",\n      },\n      {\n        name: \"token_type\",\n        type: \"string\",\n      },\n      {\n        name: \"scope\",\n        type: \"string\",\n      },\n      {\n        name: \"id_token\",\n        type: \"text\",\n      },\n      {\n        name: \"session_state\",\n        type: \"string\",\n      },\n    ],\n  },\n  {\n    name: \"nextauth_verificationTokens\",\n    columns: [\n      {\n        name: \"identifier\",\n        type: \"string\",\n      },\n      {\n        name: \"token\",\n        type: \"string\",\n      },\n      {\n        name: \"expires\",\n        type: \"datetime\",\n      },\n    ],\n  },\n  {\n    name: \"nextauth_users_accounts\",\n    columns: [\n      {\n        name: \"user\",\n        type: \"link\",\n        link: {\n          table: \"nextauth_users\",\n        },\n      },\n      {\n        name: \"account\",\n        type: \"link\",\n        link: {\n          table: \"nextauth_accounts\",\n        },\n      },\n    ],\n  },\n  {\n    name: \"nextauth_users_sessions\",\n    columns: [\n      {\n        name: \"user\",\n        type: \"link\",\n        link: {\n          table: \"nextauth_users\",\n        },\n      },\n      {\n        name: \"session\",\n        type: \"link\",\n        link: {\n          table: \"nextauth_sessions\",\n        },\n      },\n    ],\n  },\n  {\n    name: \"nextauth_sessions\",\n    columns: [\n      {\n        name: \"sessionToken\",\n        type: \"string\",\n      },\n      {\n        name: \"expires\",\n        type: \"datetime\",\n      },\n      {\n        name: \"user\",\n        type: \"link\",\n        link: {\n          table: \"nextauth_users\",\n        },\n      },\n    ],\n  },\n]\n\nconst DatabaseClient = buildClient() as ClientConstructor<any>\n\nexport class XataClient extends DatabaseClient<DatabaseSchema> {\n  constructor(options?: BaseClientOptions) {\n    super({ databaseURL: \"\", ...options }, schemas)\n  }\n}\n"
  },
  {
    "path": "packages/adapter-xata/test/index.test.ts",
    "content": "import { runBasicTests } from \"utils/adapter\"\nimport { XataClient } from \"../src/xata\"\nimport { XataAdapter } from \"../src\"\n\nconst client = new XataClient({\n  apiKey: process.env.XATA_API_KEY,\n  databaseURL: process.env.XATA_DATABASE_URL,\n})\n\nrunBasicTests({\n  adapter: XataAdapter(client),\n  db: {\n    async user(id: string) {\n      const data = await client.db.nextauth_users.filter({ id }).getFirst()\n      if (!data) return null\n      return data\n    },\n    async account({ provider, providerAccountId }) {\n      const data = await client.db.nextauth_accounts\n        .filter({ provider, providerAccountId })\n        .getFirst()\n      if (!data) return null\n      return data\n    },\n    async session(sessionToken) {\n      const data = await client.db.nextauth_sessions\n        .filter({ sessionToken })\n        .getFirst()\n      if (!data) return null\n      return data\n    },\n    async verificationToken(where) {\n      const data = await client.db.nextauth_verificationTokens\n        .filter(where)\n        .getFirst()\n      if (!data) return null\n      return data\n    },\n  },\n})\n"
  },
  {
    "path": "packages/adapter-xata/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"exclude\": [\"*.js\", \"*.d.ts\"],\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "packages/adapter-xata/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\", \"src/xata.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/xata-adapter\",\n  entryFileName: \"../xata-adapter.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/core/README.md",
    "content": "<p align=\"center\">\n   <br/>\n   <a href=\"https://authjs.dev\" target=\"_blank\"><img width=\"150px\" src=\"https://authjs.dev/img/logo-sm.png\" /></a>\n   <h3 align=\"center\">Auth.js core library</a></h3>\n   <h4 align=\"center\">Authentication for the Web.</h4>\n   <p align=\"center\" style=\"align: center;\">\n      <a href=\"https://npm.im/next-auth\">\n        <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n      </a>\n      <a href=\"https://npm.im/@auth/core\">\n        <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/core?color=green&label=@auth/core&style=flat-square\">\n      </a>\n      <a href=\"https://www.npmtrends.com/@auth/core\">\n        <img src=\"https://img.shields.io/npm/dm/@auth/core?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n      </a>\n      <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n        <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n      </a>\n   </p>\n</p>\n\n---\n\nCheck out the documentation at [authjs.dev](https://authjs.dev/reference/core).\n"
  },
  {
    "path": "packages/core/package.json",
    "content": "{\n  \"name\": \"@auth/core\",\n  \"version\": \"0.41.0\",\n  \"description\": \"Authentication for the Web.\",\n  \"keywords\": [\n    \"authentication\",\n    \"authjs\",\n    \"jwt\",\n    \"oauth\",\n    \"oidc\",\n    \"passwordless\",\n    \"standard\",\n    \"vanilla\",\n    \"webapi\"\n  ],\n  \"homepage\": \"https://authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth.git\",\n  \"author\": \"Balázs Orbán <info@balazsorban.com>\",\n  \"contributors\": [\n    \"Balázs Orbán <info@balazsorban.com>\",\n    \"Nico Domino <yo@ndo.dev>\",\n    \"Lluis Agusti <hi@llu.lu>\",\n    \"Thang Huu Vu <hi@thvu.dev>\",\n    \"Iain Collins <me@iaincollins.com>\"\n  ],\n  \"type\": \"module\",\n  \"types\": \"./index.d.ts\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"lib\",\n    \"providers\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\",\n      \"default\": \"./index.js\"\n    },\n    \"./adapters\": {\n      \"types\": \"./adapters.d.ts\",\n      \"import\": \"./adapters.js\",\n      \"default\": \"./adapters.js\"\n    },\n    \"./errors\": {\n      \"types\": \"./errors.d.ts\",\n      \"import\": \"./errors.js\",\n      \"default\": \"./errors.js\"\n    },\n    \"./jwt\": {\n      \"types\": \"./jwt.d.ts\",\n      \"import\": \"./jwt.js\",\n      \"default\": \"./jwt.js\"\n    },\n    \"./providers\": {\n      \"types\": \"./providers/index.d.ts\"\n    },\n    \"./providers/*\": {\n      \"types\": \"./providers/*.d.ts\",\n      \"import\": \"./providers/*.js\",\n      \"default\": \"./providers/*.js\"\n    },\n    \"./types\": {\n      \"types\": \"./types.d.ts\"\n    }\n  },\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"@panva/hkdf\": \"^1.2.1\",\n    \"jose\": \"^6.0.6\",\n    \"oauth4webapi\": \"^3.3.0\",\n    \"preact\": \"10.24.3\",\n    \"preact-render-to-string\": \"6.5.11\"\n  },\n  \"peerDependencies\": {\n    \"@simplewebauthn/browser\": \"^9.0.1\",\n    \"@simplewebauthn/server\": \"^9.0.2\",\n    \"nodemailer\": \"^7.0.7\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@simplewebauthn/browser\": {\n      \"optional\": true\n    },\n    \"@simplewebauthn/server\": {\n      \"optional\": true\n    },\n    \"nodemailer\": {\n      \"optional\": true\n    }\n  },\n  \"scripts\": {\n    \"build\": \"pnpm css && pnpm providers && tsc\",\n    \"clean\": \"rm -rf *.js *.d.ts* lib providers\",\n    \"css\": \"node scripts/generate-css\",\n    \"dev\": \"pnpm css && pnpm providers && tsc -w\",\n    \"test\": \"vitest run -c ../utils/vitest.config.ts\",\n    \"test:watch\": \"vitest -c ../utils/vitest.config.ts\",\n    \"providers\": \"node scripts/generate-providers\"\n  },\n  \"devDependencies\": {\n    \"@simplewebauthn/browser\": \"9.0.1\",\n    \"@simplewebauthn/server\": \"9.0.3\",\n    \"@simplewebauthn/types\": \"^9.0.1\",\n    \"@types/node\": \"18.11.10\",\n    \"@types/nodemailer\": \"6.4.6\",\n    \"@types/react\": \"18.0.37\",\n    \"autoprefixer\": \"10.4.13\",\n    \"postcss\": \"8.4.19\",\n    \"postcss-nesting\": \"^12.1.5\",\n    \"typedoc\": \"^0.25.12\",\n    \"typedoc-plugin-markdown\": \"4.0.0-next.53\"\n  }\n}\n"
  },
  {
    "path": "packages/core/scripts/generate-css.js",
    "content": "import fs from \"fs\"\nimport path from \"path\"\nimport postcss from \"postcss\"\n\nimport autoprefixer from \"autoprefixer\"\nimport postCssNested from \"postcss-nesting\"\n\nconst from = path.join(process.cwd(), \"src/lib/pages/styles.css\")\nconst css = fs.readFileSync(from)\n\nconst processedCss = await postcss([autoprefixer, postCssNested]).process(css, {\n  from,\n})\n\nfs.writeFileSync(\n  path.join(process.cwd(), \"src/lib/pages/styles.ts\"),\n  `// Generated by \\`pnpm css\\`\nexport default \\`${processedCss.css}\\`\n`\n)\n"
  },
  {
    "path": "packages/core/scripts/generate-providers.js",
    "content": "import { join } from \"path\"\nimport { readdirSync, writeFileSync } from \"fs\"\n\nconst providersPath = join(process.cwd(), \"src/providers\")\n\nconst files = readdirSync(providersPath, \"utf8\")\n\n// TODO: Autogenerate\nconst emailProvidersFile = [\n  \"email\",\n  \"forwardemail\",\n  \"mailgun\",\n  \"nodemailer\",\n  \"passkey\",\n  \"postmark\",\n  \"resend\",\n  \"sendgrid\",\n]\n\n// TODO: Autogenerate\nconst nonOAuthFile = [\n  \"provider-types\",\n  \"oauth\",\n  \"index\",\n  // Credentials\n  \"credentials\",\n  // Webauthn\n  \"webauthn\",\n  // Email providers\n  ...emailProvidersFile,\n]\n\nconst providers = files.map((file) => {\n  const strippedProviderName = file.substring(0, file.indexOf(\".\"))\n  return `\"${strippedProviderName}\"`\n})\n\nconst oauthProviders = providers.filter(\n  (provider) => !nonOAuthFile.includes(provider.replace(/\"/g, \"\"))\n)\n\nconst emailProviders = providers.filter((provider) =>\n  emailProvidersFile.includes(provider.replace(/\"/g, \"\"))\n)\n\nconst content = `\n// THIS FILE IS AUTOGENERATED. DO NOT EDIT.\nexport type OAuthProviderId = \n  | ${oauthProviders.join(\"\\n  | \")}\n\nexport type EmailProviderId = \n  | ${emailProviders.join(\"\\n  | \")}`\n\nwriteFileSync(join(providersPath, \"provider-types.ts\"), content)\n"
  },
  {
    "path": "packages/core/src/adapters.ts",
    "content": "/**\n * Auth.js can be integrated with _any_ data layer (database, ORM, or backend API, HTTP client)\n * in order to automatically create users, handle account linking automatically, support passwordless login,\n * and to store session information.\n *\n * This module contains utility functions and types to create an Auth.js compatible adapter.\n *\n * Auth.js supports 2 session strategies to persist the login state of a user.\n * The default is to use a cookie + {@link https://authjs.dev/concepts/session-strategies#jwt-session JWT}\n * based session store (`strategy: \"jwt\"`),\n * but you can also use a database adapter to store the session in a database.\n *\n * Before you continue, Auth.js has a list of {@link https://adapters.authjs.dev official database adapters}. If your database is listed there, you\n * probably do not need to create your own. If you are using a data solution that cannot be integrated with an official adapter, this module will help you create a compatible adapter.\n *\n * :::caution Note\n * Although `@auth/core` _is_ framework/runtime agnostic, an adapter might rely on a client/ORM package,\n * that is not yet compatible with your framework/runtime (e.g. it might rely on [Node.js APIs](https://nodejs.org/docs/latest/api)).\n * Related issues should be reported to the corresponding package maintainers.\n * :::\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/core\n * ```\n *\n * Then, you can import this submodule from `@auth/core/adapters`.\n *\n * ## Usage\n *\n * Each adapter method and its function signature is documented in the {@link Adapter} interface.\n *\n * ```ts title=my-adapter.ts\n * import { type Adapter } from \"@auth/core/adapters\"\n *\n * // 1. Simplest form, a plain object.\n * export const MyAdapter: Adapter {\n *  // implement the adapter methods here\n * }\n *\n * // or\n *\n * // 2. A function that returns an object. Official adapters use this pattern.\n * export function MyAdapter(config: any): Adapter {\n *  // Instantiate a client/ORM here with the provided config, or pass it in as a parameter.\n *  // Usually, you might already have a client instance elsewhere in your application,\n *  // so you should only create a new instance if you need to or you don't have one.\n *\n *  return {\n *    // implement the adapter methods\n *  }\n * }\n *\n * ```\n *\n * Then, you can pass your adapter to Auth.js as the `adapter` option.\n *\n * ```ts title=index.ts\n * import { MyAdapter } from \"./my-adapter\"\n *\n * const response = await Auth(..., {\n *   adapter: MyAdapter, // 1.\n *   // or\n *   adapter: MyAdapter({ /* config *\\/ }), // 2.\n *   ...\n * })\n * ```\n *\n * Note, you might be able to tweak an existing adapter to work with your data layer, instead of creating one from scratch.\n *\n * ```ts title=my-adapter.ts\n * import { type Adapter } from \"@auth/core/adapters\"\n * import { PrismaAdapter } from \"@auth/prisma-adapter\"\n * import { PrismaClient } from \"@prisma/client\"\n *\n * const prisma = new PrismaClient()\n *\n * const adapter: Adapter = {\n *   ...PrismaAdapter(prisma),\n *   // Add your custom methods here\n * }\n *\n * const request = new Request(\"https://example.com\")\n * const response = await Auth(request, { adapter, ... })\n * ```\n *\n * ## Models\n *\n * Auth.js can be used with any database. Models tell you what structures Auth.js expects from your database. Models will vary slightly depending on which adapter you use, but in general, will have a similar structure to the graph below. Each model can be extended with additional fields.\n *\n * :::note\n * Auth.js / NextAuth.js uses `camelCase` for its database rows while respecting the conventional `snake_case` formatting for OAuth-related values. If the mixed casing is an issue for you, most adapters have a dedicated documentation section on how to force a casing convention.\n * :::\n *\n * ```mermaid\n * erDiagram\n *     User ||--|{ Account : \"\"\n *     User {\n *       string id\n *       string name\n *       string email\n *       timestamp emailVerified\n *       string image\n *     }\n *     User ||--|{ Session : \"\"\n *     Session {\n *       string id\n *       timestamp expires\n *       string sessionToken\n *       string userId\n *     }\n *     Account {\n *       string id\n *       string userId\n *       string type\n *       string provider\n *       string providerAccountId\n *       string refresh_token\n *       string access_token\n *       int expires_at\n *       string token_type\n *       string scope\n *       string id_token\n *       string session_state\n *     }\n *     User ||--|{ VerificationToken : \"\"\n *     VerificationToken {\n *       string identifier\n *       string token\n *       timestamp expires\n *     }\n * ```\n *\n * ## Testing\n *\n * There is a test suite [available](https://github.com/nextauthjs/next-auth/blob/main/packages/utils/adapter.ts)\n * to ensure that your adapter is compatible with Auth.js.\n *\n * ## Known issues\n *\n * The following are missing built-in features in Auth.js but can be solved in user land. If you would like to help implement these features, please reach out.\n *\n * ### Token rotation\n *\n * Auth.js _currently_ does not support {@link https://authjs.dev/concepts/oauth `access_token` rotation} out of the box.\n * The necessary information (`refresh_token`, expiry, etc.) is being stored in the database, but the logic to rotate the token is not implemented\n * in the core library.\n * [This guide](https://authjs.dev/guides/refresh-token-rotation#database-strategy) should provide the necessary steps to do this in user land.\n *\n * ### Federated logout\n *\n * Auth.js _currently_ does not support federated logout out of the box.\n * This means that even if an active session is deleted from the database, the user will still be signed in to the identity provider,\n * they will only be signed out of the application.\n * Eg. if you use Google as an identity provider, and you delete the session from the database,\n * the user will still be signed in to Google, but they will be signed out of your application.\n *\n * If your users might be using the application from a publicly shared computer (eg: library), you might want to implement federated logout.\n *\n * @module adapters\n */\n\nimport { ProviderType } from \"./providers/index.js\"\nimport type { Account, Authenticator, Awaitable, User } from \"./types.js\"\n// TODO: Discuss if we should expose methods to serialize and deserialize\n// the data? Many adapters share this logic, so it could be useful to\n// have a common implementation.\n\n/**\n * A user represents a person who can sign in to the application.\n * If a user does not exist yet, it will be created when they sign in for the first time,\n * using the information (profile data) returned by the identity provider.\n * A corresponding account is also created and linked to the user.\n */\nexport interface AdapterUser extends User {\n  /** A unique identifier for the user. */\n  id: string\n  /** The user's email address. */\n  email: string\n  /**\n   * Whether the user has verified their email address via an [Email provider](https://authjs.dev/getting-started/authentication/email).\n   * It is `null` if the user has not signed in with the Email provider yet, or the date of the first successful signin.\n   */\n  emailVerified: Date | null\n}\n\n/**\n * The type of account.\n */\nexport type AdapterAccountType = Extract<\n  ProviderType,\n  \"oauth\" | \"oidc\" | \"email\" | \"webauthn\"\n>\n\n/**\n * An account is a connection between a user and a provider.\n *\n * There are two types of accounts:\n * - OAuth/OIDC accounts, which are created when a user signs in with an OAuth provider.\n * - Email accounts, which are created when a user signs in with an [Email provider](https://authjs.dev/getting-started/authentication/email).\n *\n * One user can have multiple accounts.\n */\nexport interface AdapterAccount extends Account {\n  userId: string\n  type: AdapterAccountType\n}\n\n/**\n * A session holds information about a user's current signin state.\n */\nexport interface AdapterSession {\n  /**\n   * A randomly generated value that is used to look up the session in the database\n   * when using `\"database\"` `AuthConfig.strategy` option.\n   * This value is saved in a secure, HTTP-Only cookie on the client.\n   */\n  sessionToken: string\n  /** Connects the active session to a user in the database */\n  userId: string\n  /**\n   * The absolute date when the session expires.\n   *\n   * If a session is accessed prior to its expiry date,\n   * it will be extended based on the `maxAge` option as defined in by `SessionOptions.maxAge`.\n   * It is never extended more than once in a period defined by `SessionOptions.updateAge`.\n   *\n   * If a session is accessed past its expiry date,\n   * it will be removed from the database to clean up inactive sessions.\n   *\n   */\n  expires: Date\n}\n\n/**\n * A verification token is a temporary token that is used to sign in a user via their email address.\n * It is created when a user signs in with an [Email provider](https://authjs.dev/getting-started/authentication/email).\n * When the user clicks the link in the email, the token and email is sent back to the server\n * where it is hashed and compared to the value in the database.\n * If the tokens and emails match, and the token hasn't expired yet, the user is signed in.\n * The token is then deleted from the database.\n */\nexport interface VerificationToken {\n  /** The user's email address. */\n  identifier: string\n  /** The absolute date when the token expires. */\n  expires: Date\n  /**\n   * A [hashed](https://en.wikipedia.org/wiki/Hash_function) token, using the `AuthConfig.secret` value.\n   */\n  token: string\n}\n\n/**\n * An authenticator represents a credential authenticator assigned to a user.\n */\nexport interface AdapterAuthenticator extends Authenticator {\n  /**\n   * User ID of the authenticator.\n   */\n  userId: string\n}\n\n/**\n * An adapter is an object with function properties (methods) that read and write data from a data source.\n * Think of these methods as a way to normalize the data layer to common interfaces that Auth.js can understand.\n *\n * This is what makes Auth.js very flexible and allows it to be used with any data layer.\n *\n * The adapter methods are used to perform the following operations:\n * - Create/update/delete a user\n * - Link/unlink an account to/from a user\n * - Handle active sessions\n * - Support passwordless authentication across multiple devices\n *\n * :::note\n * If any of the methods are not implemented, but are called by Auth.js,\n * an error will be shown to the user and the operation will fail.\n * :::\n */\nexport interface Adapter {\n  /**\n   * Creates a user in the database and returns it.\n   *\n   * See also [User management](https://authjs.dev/guides/creating-a-database-adapter#user-management)\n   */\n  createUser?(user: AdapterUser): Awaitable<AdapterUser>\n  /**\n   * Returns a user from the database via the user id.\n   *\n   * See also [User management](https://authjs.dev/guides/creating-a-database-adapter#user-management)\n   */\n  getUser?(id: string): Awaitable<AdapterUser | null>\n  /**\n   * Returns a user from the database via the user's email address.\n   *\n   * See also [Verification tokens](https://authjs.dev/guides/creating-a-database-adapter#verification-tokens)\n   */\n  getUserByEmail?(email: string): Awaitable<AdapterUser | null>\n  /**\n   * Using the provider id and the id of the user for a specific account, get the user.\n   *\n   * See also [User management](https://authjs.dev/guides/creating-a-database-adapter#user-management)\n   */\n  getUserByAccount?(\n    providerAccountId: Pick<AdapterAccount, \"provider\" | \"providerAccountId\">\n  ): Awaitable<AdapterUser | null>\n  /**\n   * Updates a user in the database and returns it.\n   *\n   * See also [User management](https://authjs.dev/guides/creating-a-database-adapter#user-management)\n   */\n  updateUser?(\n    user: Partial<AdapterUser> & Pick<AdapterUser, \"id\">\n  ): Awaitable<AdapterUser>\n  /**\n   * @todo This method is currently not invoked yet.\n   *\n   * See also [User management](https://authjs.dev/guides/creating-a-database-adapter#user-management)\n   */\n  deleteUser?(\n    userId: string\n  ): Promise<void> | Awaitable<AdapterUser | null | undefined>\n  /**\n   * This method is invoked internally (but optionally can be used for manual linking).\n   * It creates an [Account](https://authjs.dev/reference/core/adapters#models) in the database.\n   *\n   * See also [User management](https://authjs.dev/guides/creating-a-database-adapter#user-management)\n   */\n  linkAccount?(\n    account: AdapterAccount\n  ): Promise<void> | Awaitable<AdapterAccount | null | undefined>\n  /** @todo This method is currently not invoked yet. */\n  unlinkAccount?(\n    providerAccountId: Pick<AdapterAccount, \"provider\" | \"providerAccountId\">\n  ): Promise<void> | Awaitable<AdapterAccount | undefined>\n  /**\n   * Creates a session for the user and returns it.\n   *\n   * See also [Database Session management](https://authjs.dev/guides/creating-a-database-adapter#database-session-management)\n   */\n  createSession?(session: {\n    sessionToken: string\n    userId: string\n    expires: Date\n  }): Awaitable<AdapterSession>\n  /**\n   * Returns a session and a userfrom the database in one go.\n   *\n   * :::tip\n   * If the database supports joins, it's recommended to reduce the number of database queries.\n   * :::\n   *\n   * See also [Database Session management](https://authjs.dev/guides/creating-a-database-adapter#database-session-management)\n   */\n  getSessionAndUser?(\n    sessionToken: string\n  ): Awaitable<{ session: AdapterSession; user: AdapterUser } | null>\n  /**\n   * Updates a session in the database and returns it.\n   *\n   * See also [Database Session management](https://authjs.dev/guides/creating-a-database-adapter#database-session-management)\n   */\n  updateSession?(\n    session: Partial<AdapterSession> & Pick<AdapterSession, \"sessionToken\">\n  ): Awaitable<AdapterSession | null | undefined>\n  /**\n   * Deletes a session from the database. It is preferred that this method also\n   * returns the session that is being deleted for logging purposes.\n   *\n   * See also [Database Session management](https://authjs.dev/guides/creating-a-database-adapter#database-session-management)\n   */\n  deleteSession?(\n    sessionToken: string\n  ): Promise<void> | Awaitable<AdapterSession | null | undefined>\n  /**\n   * Creates a verification token and returns it.\n   *\n   * See also [Verification tokens](https://authjs.dev/guides/creating-a-database-adapter#verification-tokens)\n   */\n  createVerificationToken?(\n    verificationToken: VerificationToken\n  ): Awaitable<VerificationToken | null | undefined>\n  /**\n   * Return verification token from the database and deletes it\n   * so it can only be used once.\n   *\n   * See also [Verification tokens](https://authjs.dev/guides/creating-a-database-adapter#verification-tokens)\n   */\n  useVerificationToken?(params: {\n    identifier: string\n    token: string\n  }): Awaitable<VerificationToken | null>\n  /**\n   * Get account by provider account id and provider.\n   *\n   * If an account is not found, the adapter must return `null`.\n   */\n  getAccount?(\n    providerAccountId: AdapterAccount[\"providerAccountId\"],\n    provider: AdapterAccount[\"provider\"]\n  ): Awaitable<AdapterAccount | null>\n  /**\n   * Returns an authenticator from its credentialID.\n   *\n   * If an authenticator is not found, the adapter must return `null`.\n   */\n  getAuthenticator?(\n    credentialID: AdapterAuthenticator[\"credentialID\"]\n  ): Awaitable<AdapterAuthenticator | null>\n  /**\n   * Create a new authenticator.\n   *\n   * If the creation fails, the adapter must throw an error.\n   */\n  createAuthenticator?(\n    authenticator: AdapterAuthenticator\n  ): Awaitable<AdapterAuthenticator>\n  /**\n   * Returns all authenticators from a user.\n   *\n   * If a user is not found, the adapter should still return an empty array.\n   * If the retrieval fails for some other reason, the adapter must throw an error.\n   */\n  listAuthenticatorsByUserId?(\n    userId: AdapterAuthenticator[\"userId\"]\n  ): Awaitable<AdapterAuthenticator[]>\n  /**\n   * Updates an authenticator's counter.\n   *\n   * If the update fails, the adapter must throw an error.\n   */\n  updateAuthenticatorCounter?(\n    credentialID: AdapterAuthenticator[\"credentialID\"],\n    newCounter: AdapterAuthenticator[\"counter\"]\n  ): Awaitable<AdapterAuthenticator>\n}\n\n// https://github.com/honeinc/is-iso-date/blob/master/index.js\nconst isoDateRE =\n  /(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d:[0-5]\\d|Z))|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))/\n\n/** Determines if a given value can be parsed into `Date` */\nexport function isDate(value: unknown): value is string {\n  return (\n    typeof value === \"string\" &&\n    isoDateRE.test(value) &&\n    !isNaN(Date.parse(value))\n  )\n}\n\n// @ts-expect-error For compatibility with older versions of NextAuth.js\ndeclare module \"next-auth/adapters\" {\n  type JsonObject = {\n    [Key in string]?: JsonValue\n  }\n  type JsonArray = JsonValue[]\n  type JsonPrimitive = string | number | boolean | null\n  type JsonValue = JsonPrimitive | JsonObject | JsonArray\n  interface AdapterAccount {\n    type: \"oauth\" | \"email\" | \"oidc\"\n    [key: string]: JsonValue | undefined\n  }\n}\n"
  },
  {
    "path": "packages/core/src/errors.ts",
    "content": "type ErrorOptions = Error | Record<string, unknown>\n\ntype ErrorType =\n  | \"AccessDenied\"\n  | \"AdapterError\"\n  | \"CallbackRouteError\"\n  | \"ErrorPageLoop\"\n  | \"EventError\"\n  | \"InvalidCallbackUrl\"\n  | \"CredentialsSignin\"\n  | \"InvalidEndpoints\"\n  | \"InvalidCheck\"\n  | \"JWTSessionError\"\n  | \"MissingAdapter\"\n  | \"MissingAdapterMethods\"\n  | \"MissingAuthorize\"\n  | \"MissingSecret\"\n  | \"OAuthAccountNotLinked\"\n  | \"OAuthCallbackError\"\n  | \"OAuthProfileParseError\"\n  | \"SessionTokenError\"\n  | \"OAuthSignInError\"\n  | \"EmailSignInError\"\n  | \"SignOutError\"\n  | \"UnknownAction\"\n  | \"UnsupportedStrategy\"\n  | \"InvalidProvider\"\n  | \"UntrustedHost\"\n  | \"Verification\"\n  | \"MissingCSRF\"\n  | \"AccountNotLinked\"\n  | \"DuplicateConditionalUI\"\n  | \"MissingWebAuthnAutocomplete\"\n  | \"WebAuthnVerificationError\"\n  | \"ExperimentalFeatureNotEnabled\"\n\n/**\n * Base error class for all Auth.js errors.\n * It's optimized to be printed in the server logs in a nicely formatted way\n * via the [`logger.error`](https://authjs.dev/reference/core#logger) option.\n * @noInheritDoc\n */\nexport class AuthError extends Error {\n  type: ErrorType\n  /**\n   * Determines on which page an error should be handled. Typically `signIn` errors can be handled in-page.\n   * Default is `\"error\"`.\n   * @internal\n   */\n  kind?: \"signIn\" | \"error\"\n\n  cause?: Record<string, unknown> & { err?: Error }\n\n  /** @internal */\n  constructor(\n    message?: string | Error | ErrorOptions,\n    errorOptions?: ErrorOptions\n  ) {\n    if (message instanceof Error) {\n      super(undefined, {\n        cause: { err: message, ...(message.cause as any), ...errorOptions },\n      })\n    } else if (typeof message === \"string\") {\n      if (errorOptions instanceof Error) {\n        errorOptions = { err: errorOptions, ...(errorOptions.cause as any) }\n      }\n      super(message, errorOptions)\n    } else {\n      super(undefined, message)\n    }\n    this.name = this.constructor.name\n    // @ts-expect-error https://github.com/microsoft/TypeScript/issues/3841\n    this.type = this.constructor.type ?? \"AuthError\"\n    // @ts-expect-error https://github.com/microsoft/TypeScript/issues/3841\n    this.kind = this.constructor.kind ?? \"error\"\n\n    Error.captureStackTrace?.(this, this.constructor)\n    const url = `https://errors.authjs.dev#${this.type.toLowerCase()}`\n    this.message += `${this.message ? \". \" : \"\"}Read more at ${url}`\n  }\n}\n\n/**\n * Thrown when the user's sign-in attempt failed.\n * @noInheritDoc\n */\nexport class SignInError extends AuthError {\n  /** @internal */\n  static kind = \"signIn\"\n}\n\n/**\n * One of the database [`Adapter` methods](https://authjs.dev/reference/core/adapters#methods)\n * failed during execution.\n *\n * :::tip\n * If `debug: true` is set, you can check out `[auth][debug]` in the logs to learn more about the failed adapter method execution.\n * @example\n * ```sh\n * [auth][debug]: adapter_getUserByEmail\n * { \"args\": [undefined] }\n * ```\n * :::\n * @noInheritDoc\n */\nexport class AdapterError extends AuthError {\n  static type = \"AdapterError\"\n}\n\n/**\n * Thrown when the execution of the [`signIn` callback](https://authjs.dev/reference/core/types#signin) fails\n * or if it returns `false`.\n * @noInheritDoc\n */\nexport class AccessDenied extends AuthError {\n  static type = \"AccessDenied\"\n}\n\n/**\n * This error occurs when the user cannot finish login.\n * Depending on the provider type, this could have happened for multiple reasons.\n *\n * :::tip\n * Check out `[auth][details]` in the logs to know which provider failed.\n * @example\n * ```sh\n * [auth][details]: { \"provider\": \"github\" }\n * ```\n * :::\n *\n * For an [OAuth provider](https://authjs.dev/getting-started/authentication/oauth), possible causes are:\n * - The user denied access to the application\n * - There was an error parsing the OAuth Profile:\n *   Check out the provider's `profile` or `userinfo.request` method to make sure\n *   it correctly fetches the user's profile.\n * - The `signIn` or `jwt` callback methods threw an uncaught error:\n *   Check the callback method implementations.\n *\n * For an [Email provider](https://authjs.dev/getting-started/authentication/email), possible causes are:\n * - The provided email/token combination was invalid/missing:\n *   Check if the provider's `sendVerificationRequest` method correctly sends the email.\n * - The provided email/token combination has expired:\n *   Ask the user to log in again.\n * - There was an error with the database:\n *   Check the database logs.\n *\n * For a [Credentials provider](https://authjs.dev/getting-started/authentication/credentials), possible causes are:\n * - The `authorize` method threw an uncaught error:\n *   Check the provider's `authorize` method.\n * - The `signIn` or `jwt` callback methods threw an uncaught error:\n *   Check the callback method implementations.\n *\n * :::tip\n * Check out `[auth][cause]` in the error message for more details.\n * It will show the original stack trace.\n * :::\n * @noInheritDoc\n */\nexport class CallbackRouteError extends AuthError {\n  static type = \"CallbackRouteError\"\n}\n\n/**\n * Thrown when Auth.js is misconfigured and accidentally tried to require authentication on a custom error page.\n * To prevent an infinite loop, Auth.js will instead render its default error page.\n *\n * To fix this, make sure that the `error` page does not require authentication.\n *\n * Learn more at [Guide: Error pages](https://authjs.dev/guides/pages/error)\n * @noInheritDoc\n */\nexport class ErrorPageLoop extends AuthError {\n  static type = \"ErrorPageLoop\"\n}\n\n/**\n * One of the [`events` methods](https://authjs.dev/reference/core/types#eventcallbacks)\n * failed during execution.\n *\n * Make sure that the `events` methods are implemented correctly and uncaught errors are handled.\n *\n * Learn more at [`events`](https://authjs.dev/reference/core/types#eventcallbacks)\n * @noInheritDoc\n */\nexport class EventError extends AuthError {\n  static type = \"EventError\"\n}\n\n/**\n * Thrown when Auth.js is unable to verify a `callbackUrl` value.\n * The browser either disabled cookies or the `callbackUrl` is not a valid URL.\n *\n * Somebody might have tried to manipulate the callback URL that Auth.js uses to redirect the user back to the configured `callbackUrl`/page.\n * This could be a malicious hacker trying to redirect the user to a phishing site.\n * To prevent this, Auth.js checks if the callback URL is valid and throws this error if it is not.\n *\n * There is no action required, but it might be an indicator that somebody is trying to attack your application.\n * @noInheritDoc\n */\nexport class InvalidCallbackUrl extends AuthError {\n  static type = \"InvalidCallbackUrl\"\n}\n\n/**\n * Can be thrown from the `authorize` callback of the Credentials provider.\n * When an error occurs during the `authorize` callback, two things can happen:\n * 1. The user is redirected to the signin page, with `error=CredentialsSignin&code=credentials` in the URL. `code` is configurable.\n * 2. If you throw this error in a framework that handles form actions server-side, this error is thrown, instead of redirecting the user, so you'll need to handle.\n * @noInheritDoc\n */\nexport class CredentialsSignin extends SignInError {\n  static type = \"CredentialsSignin\"\n  /**\n   * The error code that is set in the `code` query parameter of the redirect URL.\n   *\n   *\n   * ⚠ NOTE: This property is going to be included in the URL, so make sure it does not hint at sensitive errors.\n   *\n   * The full error is always logged on the server, if you need to debug.\n   *\n   * Generally, we don't recommend hinting specifically if the user had either a wrong username or password specifically,\n   * try rather something like \"Invalid credentials\".\n   */\n  code: string = \"credentials\"\n}\n\n/**\n * One of the configured OAuth or OIDC providers is missing the `authorization`, `token` or `userinfo`, or `issuer` configuration.\n * To perform OAuth or OIDC sign in, at least one of these endpoints is required.\n *\n * Learn more at [`OAuth2Config`](https://authjs.dev/reference/core/providers#oauth2configprofile) or [Guide: OAuth Provider](https://authjs.dev/guides/configuring-oauth-providers)\n * @noInheritDoc\n */\nexport class InvalidEndpoints extends AuthError {\n  static type = \"InvalidEndpoints\"\n}\n\n/**\n * Thrown when a PKCE, state or nonce OAuth check could not be performed.\n * This could happen if the OAuth provider is configured incorrectly or if the browser is blocking cookies.\n *\n * Learn more at [`checks`](https://authjs.dev/reference/core/providers#checks)\n * @noInheritDoc\n */\nexport class InvalidCheck extends AuthError {\n  static type = \"InvalidCheck\"\n}\n\n/**\n * Logged on the server when Auth.js could not decode or encode a JWT-based (`strategy: \"jwt\"`) session.\n *\n * Possible causes are either a misconfigured `secret` or a malformed JWT or `encode/decode` methods.\n *\n * :::note\n * When this error is logged, the session cookie is destroyed.\n * :::\n *\n * Learn more at [`secret`](https://authjs.dev/reference/core#secret), [`jwt.encode`](https://authjs.dev/reference/core/jwt#encode-1) or [`jwt.decode`](https://authjs.dev/reference/core/jwt#decode-2) for more information.\n * @noInheritDoc\n */\nexport class JWTSessionError extends AuthError {\n  static type = \"JWTSessionError\"\n}\n\n/**\n * Thrown if Auth.js is misconfigured. This could happen if you configured an Email provider but did not set up a database adapter,\n * or tried using a `strategy: \"database\"` session without a database adapter.\n * In both cases, make sure you either remove the configuration or add the missing adapter.\n *\n * Learn more at [Database Adapters](https://authjs.dev/getting-started/database), [Email provider](https://authjs.dev/getting-started/authentication/email) or [Concept: Database session strategy](https://authjs.dev/concepts/session-strategies#database-session)\n * @noInheritDoc\n */\nexport class MissingAdapter extends AuthError {\n  static type = \"MissingAdapter\"\n}\n\n/**\n * Thrown similarily to [`MissingAdapter`](https://authjs.dev/reference/core/errors#missingadapter), but only some required methods were missing.\n *\n * Make sure you either remove the configuration or add the missing methods to the adapter.\n *\n * Learn more at [Database Adapters](https://authjs.dev/getting-started/database)\n * @noInheritDoc\n */\nexport class MissingAdapterMethods extends AuthError {\n  static type = \"MissingAdapterMethods\"\n}\n\n/**\n * Thrown when a Credentials provider is missing the `authorize` configuration.\n * To perform credentials sign in, the `authorize` method is required.\n *\n * Learn more at [Credentials provider](https://authjs.dev/getting-started/authentication/credentials)\n * @noInheritDoc\n */\nexport class MissingAuthorize extends AuthError {\n  static type = \"MissingAuthorize\"\n}\n\n/**\n * Auth.js requires a secret or multiple secrets to be set, but none was not found. This is used to encrypt cookies, JWTs and other sensitive data.\n *\n * :::note\n * If you are using a framework like Next.js, we try to automatically infer the secret from the `AUTH_SECRET`, `AUTH_SECRET_1`, etc. environment variables.\n * Alternatively, you can also explicitly set the [`AuthConfig.secret`](https://authjs.dev/reference/core#secret) option.\n * :::\n *\n *\n * :::tip\n * To generate a random string, you can use the Auth.js CLI: `npx auth secret`\n * :::\n * @noInheritDoc\n */\nexport class MissingSecret extends AuthError {\n  static type = \"MissingSecret\"\n}\n\n/**\n * Thrown when an Email address is already associated with an account\n * but the user is trying an OAuth account that is not linked to it.\n *\n * For security reasons, Auth.js does not automatically link OAuth accounts to existing accounts if the user is not signed in.\n *\n * :::tip\n * If you trust the OAuth provider to have verified the user's email address,\n * you can enable automatic account linking by setting [`allowDangerousEmailAccountLinking: true`](https://authjs.dev/reference/core/providers#allowdangerousemailaccountlinking)\n * in the provider configuration.\n * :::\n * @noInheritDoc\n */\nexport class OAuthAccountNotLinked extends SignInError {\n  static type = \"OAuthAccountNotLinked\"\n}\n\n/**\n * Thrown when an OAuth provider returns an error during the sign in process.\n * This could happen for example if the user denied access to the application or there was a configuration error.\n *\n * For a full list of possible reasons, check out the specification [Authorization Code Grant: Error Response](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1)\n * @noInheritDoc\n */\nexport class OAuthCallbackError extends SignInError {\n  static type = \"OAuthCallbackError\"\n}\n\n/**\n * This error occurs during an OAuth sign in attempt when the provider's\n * response could not be parsed. This could for example happen if the provider's API\n * changed, or the [`OAuth2Config.profile`](https://authjs.dev/reference/core/providers#oauth2configprofile) method is not implemented correctly.\n * @noInheritDoc\n */\nexport class OAuthProfileParseError extends AuthError {\n  static type = \"OAuthProfileParseError\"\n}\n\n/**\n * Logged on the server when Auth.js could not retrieve a session from the database (`strategy: \"database\"`).\n *\n * The database adapter might be misconfigured or the database is not reachable.\n *\n * Learn more at [Concept: Database session strategy](https://authjs.dev/concepts/session-strategies#database)\n * @noInheritDoc\n */\nexport class SessionTokenError extends AuthError {\n  static type = \"SessionTokenError\"\n}\n\n/**\n * Happens when login by [OAuth](https://authjs.dev/getting-started/authentication/oauth) could not be started.\n *\n * Possible causes are:\n * - The Authorization Server is not compliant with the [OAuth 2.0](https://www.ietf.org/rfc/rfc6749.html) or the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *   Check the details in the error message.\n *\n * :::tip\n * Check out `[auth][details]` in the logs to know which provider failed.\n * @example\n * ```sh\n * [auth][details]: { \"provider\": \"github\" }\n * ```\n * :::\n * @noInheritDoc\n */\nexport class OAuthSignInError extends SignInError {\n  static type = \"OAuthSignInError\"\n}\n\n/**\n * Happens when the login by an [Email provider](https://authjs.dev/getting-started/authentication/email) could not be started.\n *\n * Possible causes are:\n * - The email sent from the client is invalid, could not be normalized by [`EmailConfig.normalizeIdentifier`](https://authjs.dev/reference/core/providers/email#normalizeidentifier)\n * - The provided email/token combination has expired:\n *   Ask the user to log in again.\n * - There was an error with the database:\n *   Check the database logs.\n * @noInheritDoc\n */\nexport class EmailSignInError extends SignInError {\n  static type = \"EmailSignInError\"\n}\n\n/**\n * Represents an error that occurs during the sign-out process. This error\n * is logged when there are issues in terminating a user's session, either\n * by failing to delete the session from the database (in database session\n * strategies) or encountering issues during other parts of the sign-out\n * process, such as emitting sign-out events or clearing session cookies.\n *\n * The session cookie(s) are emptied even if this error is logged.\n * @noInheritDoc\n */\nexport class SignOutError extends AuthError {\n  static type = \"SignOutError\"\n}\n\n/**\n * Auth.js was requested to handle an operation that it does not support.\n *\n * See [`AuthAction`](https://authjs.dev/reference/core/types#authaction) for the supported actions.\n * @noInheritDoc\n */\nexport class UnknownAction extends AuthError {\n  static type = \"UnknownAction\"\n}\n\n/**\n * Thrown when a Credentials provider is present but the JWT strategy (`strategy: \"jwt\"`) is not enabled.\n *\n * Learn more at [`strategy`](https://authjs.dev/reference/core#strategy) or [Credentials provider](https://authjs.dev/getting-started/authentication/credentials)\n * @noInheritDoc\n */\nexport class UnsupportedStrategy extends AuthError {\n  static type = \"UnsupportedStrategy\"\n}\n\n/**\n * Thrown when an endpoint was incorrectly called without a provider, or with an unsupported provider.\n * @noInheritDoc\n */\nexport class InvalidProvider extends AuthError {\n  static type = \"InvalidProvider\"\n}\n\n/**\n * Thrown when the `trustHost` option was not set to `true`.\n *\n * Auth.js requires the `trustHost` option to be set to `true` since it's relying on the request headers' `host` value.\n *\n * :::note\n * Official Auth.js libraries might attempt to automatically set the `trustHost` option to `true` if the request is coming from a trusted host on a trusted platform.\n * :::\n *\n * Learn more at [`trustHost`](https://authjs.dev/reference/core#trusthost) or [Guide: Deployment](https://authjs.dev/getting-started/deployment)\n * @noInheritDoc\n */\nexport class UntrustedHost extends AuthError {\n  static type = \"UntrustedHost\"\n}\n\n/**\n * The user's email/token combination was invalid.\n * This could be because the email/token combination was not found in the database,\n * or because the token has expired. Ask the user to log in again.\n * @noInheritDoc\n */\nexport class Verification extends AuthError {\n  static type = \"Verification\"\n}\n\n/**\n * Error for missing CSRF tokens in client-side actions (`signIn`, `signOut`, `useSession#update`).\n * Thrown when actions lack the double submit cookie, essential for CSRF protection.\n *\n * CSRF ([Cross-Site Request Forgery](https://owasp.org/www-community/attacks/csrf))\n * is an attack leveraging authenticated user credentials for unauthorized actions.\n *\n * Double submit cookie pattern, a CSRF defense, requires matching values in a cookie\n * and request parameter. More on this at [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Glossary/CSRF).\n * @noInheritDoc\n */\nexport class MissingCSRF extends SignInError {\n  static type = \"MissingCSRF\"\n}\n\nconst clientErrors = new Set<ErrorType>([\n  \"CredentialsSignin\",\n  \"OAuthAccountNotLinked\",\n  \"OAuthCallbackError\",\n  \"AccessDenied\",\n  \"Verification\",\n  \"MissingCSRF\",\n  \"AccountNotLinked\",\n  \"WebAuthnVerificationError\",\n])\n\n/**\n * Used to only allow sending a certain subset of errors to the client.\n * Errors are always logged on the server, but to prevent leaking sensitive information,\n * only a subset of errors are sent to the client as-is.\n * @internal\n */\nexport function isClientError(error: Error): error is AuthError {\n  if (error instanceof AuthError) return clientErrors.has(error.type)\n  return false\n}\n/**\n * Thrown when multiple providers have `enableConditionalUI` set to `true`.\n * Only one provider can have this option enabled at a time.\n * @noInheritDoc\n */\nexport class DuplicateConditionalUI extends AuthError {\n  static type = \"DuplicateConditionalUI\"\n}\n\n/**\n * Thrown when a WebAuthn provider has `enableConditionalUI` set to `true` but no formField has `webauthn` in its autocomplete param.\n *\n * The `webauthn` autocomplete param is required for conditional UI to work.\n * @noInheritDoc\n */\nexport class MissingWebAuthnAutocomplete extends AuthError {\n  static type = \"MissingWebAuthnAutocomplete\"\n}\n\n/**\n * Thrown when a WebAuthn provider fails to verify a client response.\n * @noInheritDoc\n */\nexport class WebAuthnVerificationError extends AuthError {\n  static type = \"WebAuthnVerificationError\"\n}\n\n/**\n * Thrown when an Email address is already associated with an account\n * but the user is trying an account that is not linked to it.\n *\n * For security reasons, Auth.js does not automatically link accounts to existing accounts if the user is not signed in.\n * @noInheritDoc\n */\nexport class AccountNotLinked extends SignInError {\n  static type = \"AccountNotLinked\"\n}\n\n/**\n * Thrown when an experimental feature is used but not enabled.\n * @noInheritDoc\n */\nexport class ExperimentalFeatureNotEnabled extends AuthError {\n  static type = \"ExperimentalFeatureNotEnabled\"\n}\n"
  },
  {
    "path": "packages/core/src/index.ts",
    "content": "/**\n *\n * :::warning Experimental\n * `@auth/core` is under active development.\n * :::\n *\n * This is the main entry point to the Auth.js library.\n *\n * Based on the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Request}\n * and {@link https://developer.mozilla.org/en-US/docs/Web/API/Response Response} Web standard APIs.\n * Primarily used to implement [framework](https://authjs.dev/getting-started/integrations)-specific packages,\n * but it can also be used directly.\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/core\n * ```\n *\n * ## Usage\n *\n * ```ts\n * import { Auth } from \"@auth/core\"\n *\n * const request = new Request(\"https://example.com\")\n * const response = await Auth(request, {...})\n *\n * console.log(response instanceof Response) // true\n * ```\n *\n * ## Resources\n *\n * - [Getting started](https://authjs.dev/getting-started)\n * - [Guides](https://authjs.dev/guides)\n *\n * @module @auth/core\n */\n\nimport { assertConfig } from \"./lib/utils/assert.js\"\nimport {\n  AuthError,\n  CredentialsSignin,\n  ErrorPageLoop,\n  isClientError,\n} from \"./errors.js\"\nimport { AuthInternal, raw, skipCSRFCheck } from \"./lib/index.js\"\nimport { setEnvDefaults, createActionURL } from \"./lib/utils/env.js\"\nimport renderPage from \"./lib/pages/index.js\"\nimport { setLogger, type LoggerInstance } from \"./lib/utils/logger.js\"\nimport { toInternalRequest, toResponse } from \"./lib/utils/web.js\"\n\nimport type { Adapter, AdapterSession, AdapterUser } from \"./adapters.js\"\nimport type {\n  Account,\n  AuthAction,\n  Awaitable,\n  CookiesOptions,\n  DefaultSession,\n  PagesOptions,\n  Profile,\n  ResponseInternal,\n  Session,\n  Theme,\n  User,\n} from \"./types.js\"\nimport type { CredentialInput, Provider } from \"./providers/index.js\"\nimport { JWT, JWTOptions } from \"./jwt.js\"\nimport { isAuthAction } from \"./lib/utils/actions.js\"\n\nexport { customFetch } from \"./lib/symbols.js\"\nexport { skipCSRFCheck, raw, setEnvDefaults, createActionURL, isAuthAction }\n\nexport async function Auth(\n  request: Request,\n  config: AuthConfig & { raw: typeof raw }\n): Promise<ResponseInternal>\n\nexport async function Auth(\n  request: Request,\n  config: Omit<AuthConfig, \"raw\">\n): Promise<Response>\n\n/**\n * Core functionality provided by Auth.js.\n *\n * Receives a standard {@link Request} and returns a {@link Response}.\n *\n * @example\n * ```ts\n * import { Auth } from \"@auth/core\"\n *\n * const request = new Request(\"https://example.com\")\n * const response = await Auth(request, {\n *   providers: [Google],\n *   secret: \"...\",\n *   trustHost: true,\n * })\n *```\n * @see [Documentation](https://authjs.dev)\n */\nexport async function Auth(\n  request: Request,\n  config: AuthConfig\n): Promise<Response | ResponseInternal> {\n  const logger = setLogger(config)\n\n  const internalRequest = await toInternalRequest(request, config)\n  // There was an error parsing the request\n  if (!internalRequest) return Response.json(`Bad request.`, { status: 400 })\n\n  const warningsOrError = assertConfig(internalRequest, config)\n\n  if (Array.isArray(warningsOrError)) {\n    warningsOrError.forEach(logger.warn)\n  } else if (warningsOrError) {\n    // If there's an error in the user config, bail out early\n    logger.error(warningsOrError)\n    const htmlPages = new Set<AuthAction>([\n      \"signin\",\n      \"signout\",\n      \"error\",\n      \"verify-request\",\n    ])\n    if (\n      !htmlPages.has(internalRequest.action) ||\n      internalRequest.method !== \"GET\"\n    ) {\n      const message =\n        \"There was a problem with the server configuration. Check the server logs for more information.\"\n      return Response.json({ message }, { status: 500 })\n    }\n\n    const { pages, theme } = config\n\n    // If this is true, the config required auth on the error page\n    // which could cause a redirect loop\n    const authOnErrorPage =\n      pages?.error &&\n      internalRequest.url.searchParams\n        .get(\"callbackUrl\")\n        ?.startsWith(pages.error)\n\n    // Either there was no error page configured or the configured one contains infinite redirects\n    if (!pages?.error || authOnErrorPage) {\n      if (authOnErrorPage) {\n        logger.error(\n          new ErrorPageLoop(\n            `The error page ${pages?.error} should not require authentication`\n          )\n        )\n      }\n\n      const page = renderPage({ theme }).error(\"Configuration\")\n      return toResponse(page)\n    }\n\n    const url = `${internalRequest.url.origin}${pages.error}?error=Configuration`\n    return Response.redirect(url)\n  }\n\n  const isRedirect = request.headers?.has(\"X-Auth-Return-Redirect\")\n  const isRaw = config.raw === raw\n  try {\n    const internalResponse = await AuthInternal(internalRequest, config)\n    if (isRaw) return internalResponse\n\n    const response = toResponse(internalResponse)\n    const url = response.headers.get(\"Location\")\n\n    if (!isRedirect || !url) return response\n\n    return Response.json({ url }, { headers: response.headers })\n  } catch (e) {\n    const error = e as Error\n    logger.error(error)\n\n    const isAuthError = error instanceof AuthError\n    if (isAuthError && isRaw && !isRedirect) throw error\n\n    // If the CSRF check failed for POST/session, return a 400 status code.\n    // We should not redirect to a page as this is an API route\n    if (request.method === \"POST\" && internalRequest.action === \"session\")\n      return Response.json(null, { status: 400 })\n\n    const isClientSafeErrorType = isClientError(error)\n    const type = isClientSafeErrorType ? error.type : \"Configuration\"\n\n    const params = new URLSearchParams({ error: type })\n    if (error instanceof CredentialsSignin) params.set(\"code\", error.code)\n\n    const pageKind = (isAuthError && error.kind) || \"error\"\n    const pagePath =\n      config.pages?.[pageKind] ?? `${config.basePath}/${pageKind.toLowerCase()}`\n    const url = `${internalRequest.url.origin}${pagePath}?${params}`\n\n    if (isRedirect) return Response.json({ url })\n    return Response.redirect(url)\n  }\n}\n\n/**\n * Configure the {@link Auth} method.\n *\n * @example\n * ```ts\n * import Auth, { type AuthConfig } from \"@auth/core\"\n *\n * export const authConfig: AuthConfig = {...}\n *\n * const request = new Request(\"https://example.com\")\n * const response = await AuthHandler(request, authConfig)\n * ```\n *\n * @see [Initialization](https://authjs.dev/reference/core/types#authconfig)\n */\nexport interface AuthConfig {\n  /**\n   * List of authentication providers for signing in\n   * (e.g. Google, Facebook, Twitter, GitHub, Email, etc) in any order.\n   * This can be one of the built-in providers or an object with a custom provider.\n   *\n   * @default []\n   */\n  providers: Provider[]\n  /**\n   * A random string used to hash tokens, sign cookies and generate cryptographic keys.\n   *\n   * To generate a random string, you can use the Auth.js CLI: `npx auth secret`\n   *\n   * @note\n   * You can also pass an array of secrets, in which case the first secret that successfully\n   * decrypts the JWT will be used. This is useful for rotating secrets without invalidating existing sessions.\n   * The newer secret should be added to the start of the array, which will be used for all new sessions.\n   *\n   */\n  secret?: string | string[]\n  /**\n   * Configure your session like if you want to use JWT or a database,\n   * how long until an idle session expires, or to throttle write operations in case you are using a database.\n   */\n  session?: {\n    /**\n     * Choose how you want to save the user session.\n     * The default is `\"jwt\"`, an encrypted JWT (JWE) in the session cookie.\n     *\n     * If you use an `adapter` however, we default it to `\"database\"` instead.\n     * You can still force a JWT session by explicitly defining `\"jwt\"`.\n     *\n     * When using `\"database\"`, the session cookie will only contain a `sessionToken` value,\n     * which is used to look up the session in the database.\n     *\n     * [Documentation](https://authjs.dev/reference/core#authconfig#session) | [Adapter](https://authjs.dev/reference/core#authconfig#adapter) | [About JSON Web Tokens](https://authjs.dev/concepts/session-strategies#jwt-session)\n     */\n    strategy?: \"jwt\" | \"database\"\n    /**\n     * Relative time from now in seconds when to expire the session\n     *\n     * @default 2592000 // 30 days\n     */\n    maxAge?: number\n    /**\n     * How often the session should be updated in seconds.\n     * If set to `0`, session is updated every time.\n     *\n     * @default 86400 // 1 day\n     */\n    updateAge?: number\n    /**\n     * Generate a custom session token for database-based sessions.\n     * By default, a random UUID or string is generated depending on the Node.js version.\n     * However, you can specify your own custom string (such as CUID) to be used.\n     *\n     * @default `randomUUID` or `randomBytes.toHex` depending on the Node.js version\n     */\n    generateSessionToken?: () => string\n  }\n  /**\n   * JSON Web Tokens are enabled by default if you have not specified an {@link AuthConfig.adapter}.\n   * JSON Web Tokens are encrypted (JWE) by default. We recommend you keep this behaviour.\n   */\n  jwt?: Partial<JWTOptions>\n  /**\n   * Specify URLs to be used if you want to create custom sign in, sign out and error pages.\n   * Pages specified will override the corresponding built-in page.\n   *\n   * @default {}\n   * @example\n   *\n   * ```ts\n   *   pages: {\n   *     signIn: '/auth/signin',\n   *     signOut: '/auth/signout',\n   *     error: '/auth/error',\n   *     verifyRequest: '/auth/verify-request',\n   *     newUser: '/auth/new-user'\n   *   }\n   * ```\n   */\n  pages?: Partial<PagesOptions>\n  /**\n   * Callbacks are asynchronous functions you can use to control what happens when an action is performed.\n   * Callbacks are *extremely powerful*, especially in scenarios involving JSON Web Tokens\n   * as they **allow you to implement access controls without a database** and to **integrate with external databases or APIs**.\n   */\n  callbacks?: {\n    /**\n     * Controls whether a user is allowed to sign in or not.\n     * Returning `true` continues the sign-in flow.\n     * Returning `false` or throwing an error will stop the sign-in flow and redirect the user to the error page.\n     * Returning a string will redirect the user to the specified URL.\n     *\n     * Unhandled errors will throw an `AccessDenied` with the message set to the original error.\n     *\n     * [`AccessDenied`](https://authjs.dev/reference/core/errors#accessdenied)\n     *\n     * @example\n     * ```ts\n     * callbacks: {\n     *  async signIn({ profile }) {\n     *   // Only allow sign in for users with email addresses ending with \"yourdomain.com\"\n     *   return profile?.email?.endsWith(\"@yourdomain.com\")\n     *  }\n     * }\n     * ```\n     */\n    signIn?: (params: {\n      user: User | AdapterUser\n      account?: Account | null\n      /**\n       * If OAuth provider is used, it contains the full\n       * OAuth profile returned by your provider.\n       */\n      profile?: Profile\n      /**\n       * If Email provider is used, on the first call, it contains a\n       * `verificationRequest: true` property to indicate it is being triggered in the verification request flow.\n       * When the callback is invoked after a user has clicked on a sign in link,\n       * this property will not be present. You can check for the `verificationRequest` property\n       * to avoid sending emails to addresses or domains on a blocklist or to only explicitly generate them\n       * for email address in an allow list.\n       */\n      email?: {\n        verificationRequest?: boolean\n      }\n      /** If Credentials provider is used, it contains the user credentials */\n      credentials?: Record<string, CredentialInput>\n    }) => Awaitable<boolean | string>\n    /**\n     * This callback is called anytime the user is redirected to a callback URL (i.e. on signin or signout).\n     * By default only URLs on the same host as the origin are allowed.\n     * You can use this callback to customise that behaviour.\n     *\n     * [Documentation](https://authjs.dev/reference/core/types#redirect)\n     *\n     * @example\n     * callbacks: {\n     *   async redirect({ url, baseUrl }) {\n     *     // Allows relative callback URLs\n     *     if (url.startsWith(\"/\")) return `${baseUrl}${url}`\n     *\n     *     // Allows callback URLs on the same origin\n     *     if (new URL(url).origin === baseUrl) return url\n     *\n     *     return baseUrl\n     *   }\n     * }\n     */\n    redirect?: (params: {\n      /** URL provided as callback URL by the client */\n      url: string\n      /** Default base URL of site (can be used as fallback) */\n      baseUrl: string\n    }) => Awaitable<string>\n    /**\n     * This callback is called whenever a session is checked.\n     * (i.e. when invoking the `/api/session` endpoint, using `useSession` or `getSession`).\n     * The return value will be exposed to the client, so be careful what you return here!\n     * If you want to make anything available to the client which you've added to the token\n     * through the JWT callback, you have to explicitly return it here as well.\n     *\n     * :::note\n     * ⚠ By default, only a subset (email, name, image)\n     * of the token is returned for increased security.\n     * :::\n     *\n     * The token argument is only available when using the jwt session strategy, and the\n     * user argument is only available when using the database session strategy.\n     *\n     * [`jwt` callback](https://authjs.dev/reference/core/types#jwt)\n     *\n     * @example\n     * ```ts\n     * callbacks: {\n     *   async session({ session, token, user }) {\n     *     // Send properties to the client, like an access_token from a provider.\n     *     session.accessToken = token.accessToken\n     *\n     *     return session\n     *   }\n     * }\n     * ```\n     */\n    session?: (\n      params: ({\n        session: { user: AdapterUser } & AdapterSession\n        /** Available when {@link AuthConfig.session} is set to `strategy: \"database\"`. */\n        user: AdapterUser\n      } & {\n        session: Session\n        /** Available when {@link AuthConfig.session} is set to `strategy: \"jwt\"` */\n        token: JWT\n      }) & {\n        /**\n         * Available when using {@link AuthConfig.session} `strategy: \"database\"` and an update is triggered for the session.\n         *\n         * :::note\n         * You should validate this data before using it.\n         * :::\n         */\n        newSession: any\n        trigger?: \"update\"\n      }\n    ) => Awaitable<Session | DefaultSession>\n    /**\n     * This callback is called whenever a JSON Web Token is created (i.e. at sign in)\n     * or updated (i.e whenever a session is accessed in the client). Anything you\n     * return here will be saved in the JWT and forwarded to the session callback.\n     * There you can control what should be returned to the client. Anything else\n     * will be kept from your frontend. The JWT is encrypted by default via your\n     * AUTH_SECRET environment variable.\n     *\n     * [`session` callback](https://authjs.dev/reference/core/types#session)\n     */\n    jwt?: (params: {\n      /**\n       * When `trigger` is `\"signIn\"` or `\"signUp\"`, it will be a subset of {@link JWT},\n       * `name`, `email` and `image` will be included.\n       *\n       * Otherwise, it will be the full {@link JWT} for subsequent calls.\n       */\n      token: JWT\n      /**\n       * Either the result of the {@link OAuthConfig.profile} or the {@link CredentialsConfig.authorize} callback.\n       * @note available when `trigger` is `\"signIn\"` or `\"signUp\"`.\n       *\n       * Resources:\n       * - [Credentials Provider](https://authjs.dev/getting-started/authentication/credentials)\n       * - [User database model](https://authjs.dev/guides/creating-a-database-adapter#user-management)\n       */\n      user: User | AdapterUser\n      /**\n       * Contains information about the provider that was used to sign in.\n       * Also includes {@link TokenSet}\n       * @note available when `trigger` is `\"signIn\"` or `\"signUp\"`\n       */\n      account?: Account | null\n      /**\n       * The OAuth profile returned from your provider.\n       * (In case of OIDC it will be the decoded ID Token or /userinfo response)\n       * @note available when `trigger` is `\"signIn\"`.\n       */\n      profile?: Profile\n      /**\n       * Check why was the jwt callback invoked. Possible reasons are:\n       * - user sign-in: First time the callback is invoked, `user`, `profile` and `account` will be present.\n       * - user sign-up: a user is created for the first time in the database (when {@link AuthConfig.session}.strategy is set to `\"database\"`)\n       * - update event: Triggered by the `useSession().update` method.\n       * In case of the latter, `trigger` will be `undefined`.\n       */\n      trigger?: \"signIn\" | \"signUp\" | \"update\"\n      /** @deprecated use `trigger === \"signUp\"` instead */\n      isNewUser?: boolean\n      /**\n       * When using {@link AuthConfig.session} `strategy: \"jwt\"`, this is the data\n       * sent from the client via the `useSession().update` method.\n       *\n       * ⚠ Note, you should validate this data before using it.\n       */\n      session?: any\n    }) => Awaitable<JWT | null>\n  }\n  /**\n   * Events are asynchronous functions that do not return a response, they are useful for audit logging.\n   * You can specify a handler for any of these events below - e.g. for debugging or to create an audit log.\n   * The content of the message object varies depending on the flow\n   * (e.g. OAuth or Email authentication flow, JWT or database sessions, etc),\n   * but typically contains a user object and/or contents of the JSON Web Token\n   * and other information relevant to the event.\n   *\n   * @default {}\n   */\n  events?: {\n    /**\n     * If using a `credentials` type auth, the user is the raw response from your\n     * credential provider.\n     * For other providers, you'll get the User object from your adapter, the account,\n     * and an indicator if the user was new to your Adapter.\n     */\n    signIn?: (message: {\n      user: User\n      account?: Account | null\n      profile?: Profile\n      isNewUser?: boolean\n    }) => Awaitable<void>\n    /**\n     * The message object will contain one of these depending on\n     * if you use JWT or database persisted sessions:\n     * - `token`: The JWT for this session.\n     * - `session`: The session object from your adapter that is being ended.\n     */\n    signOut?: (\n      message:\n        | { session: Awaited<ReturnType<Required<Adapter>[\"deleteSession\"]>> }\n        | { token: Awaited<ReturnType<JWTOptions[\"decode\"]>> }\n    ) => Awaitable<void>\n    createUser?: (message: { user: User }) => Awaitable<void>\n    updateUser?: (message: { user: User }) => Awaitable<void>\n    linkAccount?: (message: {\n      user: User | AdapterUser\n      account: Account\n      profile: User | AdapterUser\n    }) => Awaitable<void>\n    /**\n     * The message object will contain one of these depending on\n     * if you use JWT or database persisted sessions:\n     * - `token`: The JWT for this session.\n     * - `session`: The session object from your adapter.\n     */\n    session?: (message: { session: Session; token: JWT }) => Awaitable<void>\n  }\n  /** You can use the adapter option to pass in your database adapter. */\n  adapter?: Adapter\n  /**\n   * Set debug to true to enable debug messages for authentication and database operations.\n   *\n   * - ⚠ If you added a custom {@link AuthConfig.logger}, this setting is ignored.\n   *\n   * @default false\n   */\n  debug?: boolean\n  /**\n   * Override any of the logger levels (`undefined` levels will use the built-in logger),\n   * and intercept logs in NextAuth. You can use this option to send NextAuth logs to a third-party logging service.\n   *\n   * @example\n   *\n   * ```ts\n   * // /auth.ts\n   * import log from \"logging-service\"\n   *\n   * export const { handlers, auth, signIn, signOut } = NextAuth({\n   *   logger: {\n   *     error(code, ...message) {\n   *       log.error(code, message)\n   *     },\n   *     warn(code, ...message) {\n   *       log.warn(code, message)\n   *     },\n   *     debug(code, ...message) {\n   *       log.debug(code, message)\n   *     }\n   *   }\n   * })\n   * ```\n   *\n   * - ⚠ When set, the {@link AuthConfig.debug} option is ignored\n   *\n   * @default console\n   */\n  logger?: Partial<LoggerInstance>\n  /** Changes the theme of built-in {@link AuthConfig.pages}. */\n  theme?: Theme\n  /**\n   * When set to `true` then all cookies set by NextAuth.js will only be accessible from HTTPS URLs.\n   * This option defaults to `false` on URLs that start with `http://` (e.g. http://localhost:3000) for developer convenience.\n   * You can manually set this option to `false` to disable this security feature and allow cookies\n   * to be accessible from non-secured URLs (this is not recommended).\n   *\n   * - ⚠ **This is an advanced option.** Advanced options are passed the same way as basic options,\n   * but **may have complex implications** or side effects.\n   * You should **try to avoid using advanced options** unless you are very comfortable using them.\n   *\n   * The default is `false` HTTP and `true` for HTTPS sites.\n   */\n  useSecureCookies?: boolean\n  /**\n   * You can override the default cookie names and options for any of the cookies used by Auth.js.\n   * You can specify one or more cookies with custom properties\n   * and missing options will use the default values defined by Auth.js.\n   * If you use this feature, you will likely want to create conditional behavior\n   * to support setting different cookies policies in development and production builds,\n   * as you will be opting out of the built-in dynamic policy.\n   *\n   * - ⚠ **This is an advanced option.** Advanced options are passed the same way as basic options,\n   * but **may have complex implications** or side effects.\n   * You should **try to avoid using advanced options** unless you are very comfortable using them.\n   *\n   * @default {}\n   */\n  cookies?: Partial<CookiesOptions>\n  /**\n   * Auth.js relies on the incoming request's `host` header to function correctly. For this reason this property needs to be set to `true`.\n   *\n   * Make sure that your deployment platform sets the `host` header safely.\n   *\n   * :::note\n   * Official Auth.js-based libraries will attempt to set this value automatically for some deployment platforms (eg.: Vercel) that are known to set the `host` header safely.\n   * :::\n   */\n  trustHost?: boolean\n  skipCSRFCheck?: typeof skipCSRFCheck\n  raw?: typeof raw\n  /**\n   * When set, during an OAuth sign-in flow,\n   * the `redirect_uri` of the authorization request\n   * will be set based on this value.\n   *\n   * This is useful if your OAuth Provider only supports a single `redirect_uri`\n   * or you want to use OAuth on preview URLs (like Vercel), where you don't know the final deployment URL beforehand.\n   *\n   * The url needs to include the full path up to where Auth.js is initialized.\n   *\n   * @note This will auto-enable the `state` {@link OAuth2Config.checks} on the provider.\n   *\n   * @example\n   * ```\n   * \"https://authjs.example.com/api/auth\"\n   * ```\n   *\n   * You can also override this individually for each provider.\n   *\n   * @example\n   * ```ts\n   * GitHub({\n   *   ...\n   *   redirectProxyUrl: \"https://github.example.com/api/auth\"\n   * })\n   * ```\n   *\n   * @default `AUTH_REDIRECT_PROXY_URL` environment variable\n   *\n   * See also: [Guide: Securing a Preview Deployment](https://authjs.dev/getting-started/deployment#securing-a-preview-deployment)\n   */\n  redirectProxyUrl?: string\n\n  /**\n   * Use this option to enable experimental features.\n   * When enabled, it will print a warning message to the console.\n   * @note Experimental features are not guaranteed to be stable and may change or be removed without notice. Please use with caution.\n   * @default {}\n   */\n  experimental?: {\n    /**\n     * Enable WebAuthn support.\n     *\n     * @default false\n     */\n    enableWebAuthn?: boolean\n  }\n  /**\n   * The base path of the Auth.js API endpoints.\n   *\n   * @default \"/api/auth\" in \"next-auth\"; \"/auth\" with all other frameworks\n   */\n  basePath?: string\n}\n"
  },
  {
    "path": "packages/core/src/jwt.ts",
    "content": "/**\n *\n *\n * This module contains functions and types\n * to encode and decode {@link https://authjs.dev/concepts/session-strategies#jwt-session JWT}s\n * issued and used by Auth.js.\n *\n * The JWT issued by Auth.js is _encrypted by default_, using the _A256CBC-HS512_ algorithm ({@link https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.5 JWE}).\n * It uses the `AUTH_SECRET` environment variable or the passed `secret` property to derive a suitable encryption key.\n *\n * :::info Note\n * Auth.js JWTs are meant to be used by the same app that issued them.\n * If you need JWT authentication for your third-party API, you should rely on your Identity Provider instead.\n * :::\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/core\n * ```\n *\n * You can then import this submodule from `@auth/core/jwt`.\n *\n * ## Usage\n *\n * :::warning Warning\n * This module *will* be refactored/changed. We do not recommend relying on it right now.\n * :::\n *\n *\n * ## Resources\n *\n * - [What is a JWT session strategy](https://authjs.dev/concepts/session-strategies#jwt-session)\n * - [RFC7519 - JSON Web Token (JWT)](https://www.rfc-editor.org/rfc/rfc7519)\n *\n * @module jwt\n */\n\nimport { hkdf } from \"@panva/hkdf\"\nimport { EncryptJWT, base64url, calculateJwkThumbprint, jwtDecrypt } from \"jose\"\nimport { defaultCookies, SessionStore } from \"./lib/utils/cookie.js\"\nimport { Awaitable } from \"./types.js\"\nimport type { LoggerInstance } from \"./lib/utils/logger.js\"\nimport { MissingSecret } from \"./errors.js\"\nimport * as cookie from \"./lib/vendored/cookie.js\"\n\nconst { parse: parseCookie } = cookie\nconst DEFAULT_MAX_AGE = 30 * 24 * 60 * 60 // 30 days\n\nconst now = () => (Date.now() / 1000) | 0\n\nconst alg = \"dir\"\nconst enc = \"A256CBC-HS512\"\ntype Digest = Parameters<typeof calculateJwkThumbprint>[1]\n\n/** Issues a JWT. By default, the JWT is encrypted using \"A256CBC-HS512\". */\nexport async function encode<Payload = JWT>(params: JWTEncodeParams<Payload>) {\n  const { token = {}, secret, maxAge = DEFAULT_MAX_AGE, salt } = params\n  const secrets = Array.isArray(secret) ? secret : [secret]\n  const encryptionSecret = await getDerivedEncryptionKey(enc, secrets[0], salt)\n\n  const thumbprint = await calculateJwkThumbprint(\n    { kty: \"oct\", k: base64url.encode(encryptionSecret) },\n    `sha${encryptionSecret.byteLength << 3}` as Digest\n  )\n  // @ts-expect-error `jose` allows any object as payload.\n  return await new EncryptJWT(token)\n    .setProtectedHeader({ alg, enc, kid: thumbprint })\n    .setIssuedAt()\n    .setExpirationTime(now() + maxAge)\n    .setJti(crypto.randomUUID())\n    .encrypt(encryptionSecret)\n}\n\n/** Decodes an Auth.js issued JWT. */\nexport async function decode<Payload = JWT>(\n  params: JWTDecodeParams\n): Promise<Payload | null> {\n  const { token, secret, salt } = params\n  const secrets = Array.isArray(secret) ? secret : [secret]\n  if (!token) return null\n  const { payload } = await jwtDecrypt(\n    token,\n    async ({ kid, enc }) => {\n      for (const secret of secrets) {\n        const encryptionSecret = await getDerivedEncryptionKey(\n          enc,\n          secret,\n          salt\n        )\n        if (kid === undefined) return encryptionSecret\n\n        const thumbprint = await calculateJwkThumbprint(\n          { kty: \"oct\", k: base64url.encode(encryptionSecret) },\n          `sha${encryptionSecret.byteLength << 3}` as Digest\n        )\n        if (kid === thumbprint) return encryptionSecret\n      }\n\n      throw new Error(\"no matching decryption secret\")\n    },\n    {\n      clockTolerance: 15,\n      keyManagementAlgorithms: [alg],\n      contentEncryptionAlgorithms: [enc, \"A256GCM\"],\n    }\n  )\n  return payload as Payload\n}\n\ntype GetTokenParamsBase = {\n  secret?: JWTDecodeParams[\"secret\"]\n  salt?: JWTDecodeParams[\"salt\"]\n}\n\nexport interface GetTokenParams<R extends boolean = false>\n  extends GetTokenParamsBase {\n  /** The request containing the JWT either in the cookies or in the `Authorization` header. */\n  req: Request | { headers: Headers | Record<string, string> }\n  /**\n   * Use secure prefix for cookie name, unless URL in `NEXTAUTH_URL` is http://\n   * or not set (e.g. development or test instance) case use unprefixed name\n   */\n  secureCookie?: boolean\n  /** If the JWT is in the cookie, what name `getToken()` should look for. */\n  cookieName?: string\n  /**\n   * `getToken()` will return the raw JWT if this is set to `true`\n   *\n   * @default false\n   */\n  raw?: R\n  decode?: JWTOptions[\"decode\"]\n  logger?: LoggerInstance | Console\n}\n\n/**\n * Takes an Auth.js request (`req`) and returns either the Auth.js issued JWT's payload,\n * or the raw JWT string. We look for the JWT in the either the cookies, or the `Authorization` header.\n */\nexport async function getToken<R extends boolean = false>(\n  params: GetTokenParams<R>\n): Promise<R extends true ? string : JWT | null>\nexport async function getToken(\n  params: GetTokenParams\n): Promise<string | JWT | null> {\n  const {\n    secureCookie,\n    cookieName = defaultCookies(secureCookie ?? false).sessionToken.name,\n    decode: _decode = decode,\n    salt = cookieName,\n    secret,\n    logger = console,\n    raw,\n    req,\n  } = params\n\n  if (!req) throw new Error(\"Must pass `req` to JWT getToken()\")\n\n  const headers =\n    req.headers instanceof Headers ? req.headers : new Headers(req.headers)\n\n  const sessionStore = new SessionStore(\n    { name: cookieName, options: { secure: secureCookie } },\n    parseCookie(headers.get(\"cookie\") ?? \"\"),\n    logger\n  )\n\n  let token = sessionStore.value\n\n  const authorizationHeader = headers.get(\"authorization\")\n\n  if (!token && authorizationHeader?.split(\" \")[0] === \"Bearer\") {\n    const urlEncodedToken = authorizationHeader.split(\" \")[1]\n    token = decodeURIComponent(urlEncodedToken)\n  }\n\n  if (!token) return null\n\n  if (raw) return token\n\n  if (!secret)\n    throw new MissingSecret(\"Must pass `secret` if not set to JWT getToken()\")\n\n  try {\n    return await _decode({ token, secret, salt })\n  } catch {\n    return null\n  }\n}\n\nasync function getDerivedEncryptionKey(\n  enc: string,\n  keyMaterial: Parameters<typeof hkdf>[1],\n  salt: Parameters<typeof hkdf>[2]\n) {\n  let length: number\n  switch (enc) {\n    case \"A256CBC-HS512\":\n      length = 64\n      break\n    case \"A256GCM\":\n      length = 32\n      break\n    default:\n      throw new Error(\"Unsupported JWT Content Encryption Algorithm\")\n  }\n  return await hkdf(\n    \"sha256\",\n    keyMaterial,\n    salt,\n    `Auth.js Generated Encryption Key (${salt})`,\n    length\n  )\n}\n\nexport interface DefaultJWT extends Record<string, unknown> {\n  name?: string | null\n  email?: string | null\n  picture?: string | null\n  sub?: string\n  iat?: number\n  exp?: number\n  jti?: string\n}\n\n/**\n * Returned by the `jwt` callback when using JWT sessions\n *\n * [`jwt` callback](https://authjs.dev/reference/core/types#jwt)\n */\nexport interface JWT extends Record<string, unknown>, DefaultJWT {}\n\nexport interface JWTEncodeParams<Payload = JWT> {\n  /**\n   * The maximum age of the Auth.js issued JWT in seconds.\n   *\n   * @default 30 * 24 * 60 * 60 // 30 days\n   */\n  maxAge?: number\n  /** Used in combination with `secret`, to derive the encryption secret for JWTs. */\n  salt: string\n  /** Used in combination with `salt`, to derive the encryption secret for JWTs. */\n  secret: string | string[]\n  /** The JWT payload. */\n  token?: Payload\n}\n\nexport interface JWTDecodeParams {\n  /** Used in combination with `secret`, to derive the encryption secret for JWTs. */\n  salt: string\n  /**\n   * Used in combination with `salt`, to derive the encryption secret for JWTs.\n   *\n   * @note\n   * You can also pass an array of secrets, in which case the first secret that successfully\n   * decrypts the JWT will be used. This is useful for rotating secrets without invalidating existing sessions.\n   * The newer secret should be added to the start of the array, which will be used for all new sessions.\n   */\n  secret: string | string[]\n  /** The Auth.js issued JWT to be decoded */\n  token?: string\n}\n\nexport interface JWTOptions {\n  /**\n   * The secret used to encode/decode the Auth.js issued JWT.\n   * It can be an array of secrets, in which case the first secret that successfully\n   * decrypts the JWT will be used. This is useful for rotating secrets without invalidating existing sessions.\n   * @internal\n   */\n  secret: string | string[]\n  /**\n   * The maximum age of the Auth.js issued JWT in seconds.\n   *\n   * @default 30 * 24 * 60 * 60 // 30 days\n   */\n  maxAge: number\n  /** Override this method to control the Auth.js issued JWT encoding. */\n  encode: (params: JWTEncodeParams) => Awaitable<string>\n  /** Override this method to control the Auth.js issued JWT decoding. */\n  decode: (params: JWTDecodeParams) => Awaitable<JWT | null>\n}\n"
  },
  {
    "path": "packages/core/src/lib/actions/callback/handle-login.ts",
    "content": "import { AccountNotLinked, OAuthAccountNotLinked } from \"../../../errors.js\"\nimport { fromDate } from \"../../utils/date.js\"\n\nimport type {\n  AdapterAccount,\n  AdapterSession,\n  AdapterUser,\n} from \"../../../adapters.js\"\nimport type { Account, InternalOptions, User } from \"../../../types.js\"\nimport type { JWT } from \"../../../jwt.js\"\nimport type { OAuthConfig } from \"../../../providers/index.js\"\nimport type { SessionToken } from \"../../utils/cookie.js\"\n\n/**\n * This function handles the complex flow of signing users in, and either creating,\n * linking (or not linking) accounts depending on if the user is currently logged\n * in, if they have account already and the authentication mechanism they are using.\n *\n * It prevents insecure behaviour, such as linking OAuth accounts unless a user is\n * signed in and authenticated with an existing valid account.\n *\n * All verification (e.g. OAuth flows or email address verification flows) are\n * done prior to this handler being called to avoid additional complexity in this\n * handler.\n */\nexport async function handleLoginOrRegister(\n  sessionToken: SessionToken,\n  _profile: User | AdapterUser | { email: string },\n  _account: AdapterAccount | Account | null,\n  options: InternalOptions\n) {\n  // Input validation\n  if (!_account?.providerAccountId || !_account.type)\n    throw new Error(\"Missing or invalid provider account\")\n  if (![\"email\", \"oauth\", \"oidc\", \"webauthn\"].includes(_account.type))\n    throw new Error(\"Provider not supported\")\n\n  const {\n    adapter,\n    jwt,\n    events,\n    session: { strategy: sessionStrategy, generateSessionToken },\n  } = options\n\n  // If no adapter is configured then we don't have a database and cannot\n  // persist data; in this mode we just return a dummy session object.\n  if (!adapter) {\n    return { user: _profile as User, account: _account as Account }\n  }\n\n  const profile = _profile as AdapterUser\n  let account = _account as AdapterAccount\n\n  const {\n    createUser,\n    updateUser,\n    getUser,\n    getUserByAccount,\n    getUserByEmail,\n    linkAccount,\n    createSession,\n    getSessionAndUser,\n    deleteSession,\n  } = adapter\n\n  let session: AdapterSession | JWT | null = null\n  let user: AdapterUser | null = null\n  let isNewUser = false\n\n  const useJwtSession = sessionStrategy === \"jwt\"\n\n  if (sessionToken) {\n    if (useJwtSession) {\n      try {\n        const salt = options.cookies.sessionToken.name\n        session = await jwt.decode({ ...jwt, token: sessionToken, salt })\n        if (session && \"sub\" in session && session.sub) {\n          user = await getUser(session.sub)\n        }\n      } catch {\n        // If session can't be verified, treat as no session\n      }\n    } else {\n      const userAndSession = await getSessionAndUser(sessionToken)\n      if (userAndSession) {\n        session = userAndSession.session\n        user = userAndSession.user\n      }\n    }\n  }\n\n  if (account.type === \"email\") {\n    // If signing in with an email, check if an account with the same email address exists already\n    const userByEmail = await getUserByEmail(profile.email)\n    if (userByEmail) {\n      // If they are not already signed in as the same user, this flow will\n      // sign them out of the current session and sign them in as the new user\n      if (user?.id !== userByEmail.id && !useJwtSession && sessionToken) {\n        // Delete existing session if they are currently signed in as another user.\n        // This will switch user accounts for the session in cases where the user was\n        // already logged in with a different account.\n        await deleteSession(sessionToken)\n      }\n\n      // Update emailVerified property on the user object\n      user = await updateUser({\n        id: userByEmail.id,\n        emailVerified: new Date(),\n      })\n      await events.updateUser?.({ user })\n    } else {\n      // Create user account if there isn't one for the email address already\n      user = await createUser({ ...profile, emailVerified: new Date() })\n      await events.createUser?.({ user })\n      isNewUser = true\n    }\n\n    // Create new session\n    session = useJwtSession\n      ? {}\n      : await createSession({\n          sessionToken: generateSessionToken(),\n          userId: user.id,\n          expires: fromDate(options.session.maxAge),\n        })\n\n    return { session, user, isNewUser }\n  } else if (account.type === \"webauthn\") {\n    // Check if the account exists\n    const userByAccount = await getUserByAccount({\n      providerAccountId: account.providerAccountId,\n      provider: account.provider,\n    })\n    if (userByAccount) {\n      if (user) {\n        // If the user is already signed in with this account, we don't need to do anything\n        if (userByAccount.id === user.id) {\n          const currentAccount: AdapterAccount = { ...account, userId: user.id }\n          return { session, user, isNewUser, account: currentAccount }\n        }\n        // If the user is currently signed in, but the new account they are signing in\n        // with is already associated with another user, then we cannot link them\n        // and need to return an error.\n        throw new AccountNotLinked(\n          \"The account is already associated with another user\",\n          { provider: account.provider }\n        )\n      }\n      // If there is no active session, but the account being signed in with is already\n      // associated with a valid user then create session to sign the user in.\n      session = useJwtSession\n        ? {}\n        : await createSession({\n            sessionToken: generateSessionToken(),\n            userId: userByAccount.id,\n            expires: fromDate(options.session.maxAge),\n          })\n\n      const currentAccount: AdapterAccount = {\n        ...account,\n        userId: userByAccount.id,\n      }\n      return {\n        session,\n        user: userByAccount,\n        isNewUser,\n        account: currentAccount,\n      }\n    } else {\n      // If the account doesn't exist, we'll create it\n      if (user) {\n        // If the user is already signed in and the account isn't already associated\n        // with another user account then we can go ahead and link the accounts safely.\n        await linkAccount({ ...account, userId: user.id })\n        await events.linkAccount?.({ user, account, profile })\n\n        // As they are already signed in, we don't need to do anything after linking them\n        const currentAccount: AdapterAccount = { ...account, userId: user.id }\n        return { session, user, isNewUser, account: currentAccount }\n      }\n\n      // If the user is not signed in and it looks like a new account then we\n      // check there also isn't an user account already associated with the same\n      // email address as the one in the request.\n      const userByEmail = profile.email\n        ? await getUserByEmail(profile.email)\n        : null\n      if (userByEmail) {\n        // We don't trust user-provided email addresses, so we don't want to link accounts\n        // if the email address associated with the new account is already associated with\n        // an existing account.\n        throw new AccountNotLinked(\n          \"Another account already exists with the same e-mail address\",\n          { provider: account.provider }\n        )\n      } else {\n        // If the current user is not logged in and the profile isn't linked to any user\n        // accounts (by email or provider account id)...\n        //\n        // If no account matching the same [provider].id or .email exists, we can\n        // create a new account for the user, link it to the OAuth account and\n        // create a new session for them so they are signed in with it.\n        user = await createUser({ ...profile })\n      }\n      await events.createUser?.({ user })\n\n      await linkAccount({ ...account, userId: user.id })\n      await events.linkAccount?.({ user, account, profile })\n\n      session = useJwtSession\n        ? {}\n        : await createSession({\n            sessionToken: generateSessionToken(),\n            userId: user.id,\n            expires: fromDate(options.session.maxAge),\n          })\n\n      const currentAccount: AdapterAccount = { ...account, userId: user.id }\n      return { session, user, isNewUser: true, account: currentAccount }\n    }\n  }\n\n  // If signing in with OAuth account, check to see if the account exists already\n  const userByAccount = await getUserByAccount({\n    providerAccountId: account.providerAccountId,\n    provider: account.provider,\n  })\n  if (userByAccount) {\n    if (user) {\n      // If the user is already signed in with this account, we don't need to do anything\n      if (userByAccount.id === user.id) {\n        return { session, user, isNewUser }\n      }\n      // If the user is currently signed in, but the new account they are signing in\n      // with is already associated with another user, then we cannot link them\n      // and need to return an error.\n      throw new OAuthAccountNotLinked(\n        \"The account is already associated with another user\",\n        { provider: account.provider }\n      )\n    }\n    // If there is no active session, but the account being signed in with is already\n    // associated with a valid user then create session to sign the user in.\n    session = useJwtSession\n      ? {}\n      : await createSession({\n          sessionToken: generateSessionToken(),\n          userId: userByAccount.id,\n          expires: fromDate(options.session.maxAge),\n        })\n\n    return { session, user: userByAccount, isNewUser }\n  } else {\n    const { provider: p } = options as InternalOptions<\"oauth\" | \"oidc\">\n    const { type, provider, providerAccountId, userId, ...tokenSet } = account\n    const defaults = { providerAccountId, provider, type, userId }\n    account = Object.assign(p.account(tokenSet) ?? {}, defaults)\n\n    if (user) {\n      // If the user is already signed in and the OAuth account isn't already associated\n      // with another user account then we can go ahead and link the accounts safely.\n      await linkAccount({ ...account, userId: user.id })\n      await events.linkAccount?.({ user, account, profile })\n\n      // As they are already signed in, we don't need to do anything after linking them\n      return { session, user, isNewUser }\n    }\n\n    // If the user is not signed in and it looks like a new OAuth account then we\n    // check there also isn't an user account already associated with the same\n    // email address as the one in the OAuth profile.\n    //\n    // This step is often overlooked in OAuth implementations, but covers the following cases:\n    //\n    // 1. It makes it harder for someone to accidentally create two accounts.\n    //    e.g. by signin in with email, then again with an oauth account connected to the same email.\n    // 2. It makes it harder to hijack a user account using a 3rd party OAuth account.\n    //    e.g. by creating an oauth account then changing the email address associated with it.\n    //\n    // It's quite common for services to automatically link accounts in this case, but it's\n    // better practice to require the user to sign in *then* link accounts to be sure\n    // someone is not exploiting a problem with a third party OAuth service.\n    //\n    // OAuth providers should require email address verification to prevent this, but in\n    // practice that is not always the case; this helps protect against that.\n    const userByEmail = profile.email\n      ? await getUserByEmail(profile.email)\n      : null\n    if (userByEmail) {\n      const provider = options.provider as OAuthConfig<any>\n      if (provider?.allowDangerousEmailAccountLinking) {\n        // If you trust the oauth provider to correctly verify email addresses, you can opt-in to\n        // account linking even when the user is not signed-in.\n        user = userByEmail\n        isNewUser = false\n      } else {\n        // We end up here when we don't have an account with the same [provider].id *BUT*\n        // we do already have an account with the same email address as the one in the\n        // OAuth profile the user has just tried to sign in with.\n        //\n        // We don't want to have two accounts with the same email address, and we don't\n        // want to link them in case it's not safe to do so, so instead we prompt the user\n        // to sign in via email to verify their identity and then link the accounts.\n        throw new OAuthAccountNotLinked(\n          \"Another account already exists with the same e-mail address\",\n          { provider: account.provider }\n        )\n      }\n    } else {\n      // If the current user is not logged in and the profile isn't linked to any user\n      // accounts (by email or provider account id)...\n      //\n      // If no account matching the same [provider].id or .email exists, we can\n      // create a new account for the user, link it to the OAuth account and\n      // create a new session for them so they are signed in with it.\n      user = await createUser({ ...profile, emailVerified: null })\n      isNewUser = true\n    }\n    await events.createUser?.({ user })\n\n    await linkAccount({ ...account, userId: user.id })\n    await events.linkAccount?.({ user, account, profile })\n\n    session = useJwtSession\n      ? {}\n      : await createSession({\n          sessionToken: generateSessionToken(),\n          userId: user.id,\n          expires: fromDate(options.session.maxAge),\n        })\n\n    return { session, user, isNewUser }\n  }\n}\n"
  },
  {
    "path": "packages/core/src/lib/actions/callback/index.ts",
    "content": "// TODO: Make this file smaller\n\nimport {\n  AuthError,\n  AccessDenied,\n  CallbackRouteError,\n  CredentialsSignin,\n  InvalidProvider,\n  Verification,\n} from \"../../../errors.js\"\nimport { handleLoginOrRegister } from \"./handle-login.js\"\nimport { handleOAuth } from \"./oauth/callback.js\"\nimport { state } from \"./oauth/checks.js\"\nimport { createHash } from \"../../utils/web.js\"\n\nimport type { AdapterSession } from \"../../../adapters.js\"\nimport type {\n  Account,\n  Authenticator,\n  InternalOptions,\n  RequestInternal,\n  ResponseInternal,\n  User,\n} from \"../../../types.js\"\nimport type { Cookie, SessionStore } from \"../../utils/cookie.js\"\nimport {\n  assertInternalOptionsWebAuthn,\n  verifyAuthenticate,\n  verifyRegister,\n} from \"../../utils/webauthn-utils.js\"\n\n/** Handle callbacks from login services */\nexport async function callback(\n  request: RequestInternal,\n  options: InternalOptions,\n  sessionStore: SessionStore,\n  cookies: Cookie[]\n): Promise<ResponseInternal> {\n  if (!options.provider)\n    throw new InvalidProvider(\"Callback route called without provider\")\n  const { query, body, method, headers } = request\n  const {\n    provider,\n    adapter,\n    url,\n    callbackUrl,\n    pages,\n    jwt,\n    events,\n    callbacks,\n    session: { strategy: sessionStrategy, maxAge: sessionMaxAge },\n    logger,\n  } = options\n\n  const useJwtSession = sessionStrategy === \"jwt\"\n\n  try {\n    if (provider.type === \"oauth\" || provider.type === \"oidc\") {\n      // Use body if the response mode is set to form_post. For all other cases, use query\n      const params =\n        provider.authorization?.url.searchParams.get(\"response_mode\") ===\n        \"form_post\"\n          ? body\n          : query\n\n      // If we have a state and we are on a redirect proxy, we try to parse it\n      // and see if it contains a valid origin to redirect to. If it does, we\n      // redirect the user to that origin with the original state.\n      if (options.isOnRedirectProxy && params?.state) {\n        // NOTE: We rely on the state being encrypted using a shared secret\n        // between the proxy and the original server.\n        const parsedState = await state.decode(params.state, options)\n        const shouldRedirect =\n          parsedState?.origin &&\n          new URL(parsedState.origin).origin !== options.url.origin\n        if (shouldRedirect) {\n          const proxyRedirect = `${parsedState.origin}?${new URLSearchParams(params)}`\n          logger.debug(\"Proxy redirecting to\", proxyRedirect)\n          return { redirect: proxyRedirect, cookies }\n        }\n      }\n\n      const authorizationResult = await handleOAuth(\n        params,\n        request.cookies,\n        options\n      )\n\n      if (authorizationResult.cookies.length) {\n        cookies.push(...authorizationResult.cookies)\n      }\n\n      logger.debug(\"authorization result\", authorizationResult)\n\n      const {\n        user: userFromProvider,\n        account,\n        profile: OAuthProfile,\n      } = authorizationResult\n\n      // If we don't have a profile object then either something went wrong\n      // or the user cancelled signing in. We don't know which, so we just\n      // direct the user to the signin page for now. We could do something\n      // else in future.\n      // TODO: Handle user cancelling signin\n      if (!userFromProvider || !account || !OAuthProfile) {\n        return { redirect: `${url}/signin`, cookies }\n      }\n\n      // Check if user is allowed to sign in\n      // Attempt to get Profile from OAuth provider details before invoking\n      // signIn callback - but if no user object is returned, that is fine\n      // (that just means it's a new user signing in for the first time).\n      let userByAccount\n      if (adapter) {\n        const { getUserByAccount } = adapter\n        userByAccount = await getUserByAccount({\n          providerAccountId: account.providerAccountId,\n          provider: provider.id,\n        })\n      }\n\n      const redirect = await handleAuthorized(\n        {\n          user: userByAccount ?? userFromProvider,\n          account,\n          profile: OAuthProfile,\n        },\n        options\n      )\n      if (redirect) return { redirect, cookies }\n\n      const { user, session, isNewUser } = await handleLoginOrRegister(\n        sessionStore.value,\n        userFromProvider,\n        account,\n        options\n      )\n\n      if (useJwtSession) {\n        const defaultToken = {\n          name: user.name,\n          email: user.email,\n          picture: user.image,\n          sub: user.id?.toString(),\n        }\n        const token = await callbacks.jwt({\n          token: defaultToken,\n          user,\n          account,\n          profile: OAuthProfile,\n          isNewUser,\n          trigger: isNewUser ? \"signUp\" : \"signIn\",\n        })\n\n        // Clear cookies if token is null\n        if (token === null) {\n          cookies.push(...sessionStore.clean())\n        } else {\n          const salt = options.cookies.sessionToken.name\n          // Encode token\n          const newToken = await jwt.encode({ ...jwt, token, salt })\n\n          // Set cookie expiry date\n          const cookieExpires = new Date()\n          cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)\n\n          const sessionCookies = sessionStore.chunk(newToken, {\n            expires: cookieExpires,\n          })\n          cookies.push(...sessionCookies)\n        }\n      } else {\n        // Save Session Token in cookie\n        cookies.push({\n          name: options.cookies.sessionToken.name,\n          value: (session as AdapterSession).sessionToken,\n          options: {\n            ...options.cookies.sessionToken.options,\n            expires: (session as AdapterSession).expires,\n          },\n        })\n      }\n\n      await events.signIn?.({\n        user,\n        account,\n        profile: OAuthProfile,\n        isNewUser,\n      })\n\n      // Handle first logins on new accounts\n      // e.g. option to send users to a new account landing page on initial login\n      // Note that the callback URL is preserved, so the journey can still be resumed\n      if (isNewUser && pages.newUser) {\n        return {\n          redirect: `${pages.newUser}${\n            pages.newUser.includes(\"?\") ? \"&\" : \"?\"\n          }${new URLSearchParams({ callbackUrl })}`,\n          cookies,\n        }\n      }\n\n      return { redirect: callbackUrl, cookies }\n    } else if (provider.type === \"email\") {\n      const paramToken = query?.token as string | undefined\n      const paramIdentifier = query?.email as string | undefined\n\n      if (!paramToken) {\n        const e = new TypeError(\n          \"Missing token. The sign-in URL was manually opened without token or the link was not sent correctly in the email.\",\n          { cause: { hasToken: !!paramToken } }\n        )\n        e.name = \"Configuration\"\n        throw e\n      }\n\n      const secret = provider.secret ?? options.secret\n      // @ts-expect-error -- Verified in `assertConfig`.\n      const invite = await adapter.useVerificationToken({\n        // @ts-expect-error User-land adapters might decide to omit the identifier during lookup\n        identifier: paramIdentifier, // TODO: Drop this requirement for lookup in official adapters too\n        token: await createHash(`${paramToken}${secret}`),\n      })\n\n      const hasInvite = !!invite\n      const expired = hasInvite && invite.expires.valueOf() < Date.now()\n      const invalidInvite =\n        !hasInvite ||\n        expired ||\n        // The user might have configured the link to not contain the identifier\n        // so we only compare if it exists\n        (paramIdentifier && invite.identifier !== paramIdentifier)\n      if (invalidInvite) throw new Verification({ hasInvite, expired })\n\n      const { identifier } = invite\n      const user = (await adapter!.getUserByEmail(identifier)) ?? {\n        id: crypto.randomUUID(),\n        email: identifier,\n        emailVerified: null,\n      }\n\n      const account: Account = {\n        providerAccountId: user.email,\n        userId: user.id,\n        type: \"email\" as const,\n        provider: provider.id,\n      }\n\n      const redirect = await handleAuthorized({ user, account }, options)\n      if (redirect) return { redirect, cookies }\n\n      // Sign user in\n      const {\n        user: loggedInUser,\n        session,\n        isNewUser,\n      } = await handleLoginOrRegister(\n        sessionStore.value,\n        user,\n        account,\n        options\n      )\n\n      if (useJwtSession) {\n        const defaultToken = {\n          name: loggedInUser.name,\n          email: loggedInUser.email,\n          picture: loggedInUser.image,\n          sub: loggedInUser.id?.toString(),\n        }\n        const token = await callbacks.jwt({\n          token: defaultToken,\n          user: loggedInUser,\n          account,\n          isNewUser,\n          trigger: isNewUser ? \"signUp\" : \"signIn\",\n        })\n\n        // Clear cookies if token is null\n        if (token === null) {\n          cookies.push(...sessionStore.clean())\n        } else {\n          const salt = options.cookies.sessionToken.name\n          // Encode token\n          const newToken = await jwt.encode({ ...jwt, token, salt })\n\n          // Set cookie expiry date\n          const cookieExpires = new Date()\n          cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)\n\n          const sessionCookies = sessionStore.chunk(newToken, {\n            expires: cookieExpires,\n          })\n          cookies.push(...sessionCookies)\n        }\n      } else {\n        // Save Session Token in cookie\n        cookies.push({\n          name: options.cookies.sessionToken.name,\n          value: (session as AdapterSession).sessionToken,\n          options: {\n            ...options.cookies.sessionToken.options,\n            expires: (session as AdapterSession).expires,\n          },\n        })\n      }\n\n      await events.signIn?.({ user: loggedInUser, account, isNewUser })\n\n      // Handle first logins on new accounts\n      // e.g. option to send users to a new account landing page on initial login\n      // Note that the callback URL is preserved, so the journey can still be resumed\n      if (isNewUser && pages.newUser) {\n        return {\n          redirect: `${pages.newUser}${\n            pages.newUser.includes(\"?\") ? \"&\" : \"?\"\n          }${new URLSearchParams({ callbackUrl })}`,\n          cookies,\n        }\n      }\n\n      // Callback URL is already verified at this point, so safe to use if specified\n      return { redirect: callbackUrl, cookies }\n    } else if (provider.type === \"credentials\" && method === \"POST\") {\n      const credentials = body ?? {}\n\n      // TODO: Forward the original request as is, instead of reconstructing it\n      Object.entries(query ?? {}).forEach(([k, v]) =>\n        url.searchParams.set(k, v)\n      )\n      const userFromAuthorize = await provider.authorize(\n        credentials,\n        // prettier-ignore\n        new Request(url, { headers, method, body: JSON.stringify(body) })\n      )\n      const user = userFromAuthorize\n\n      if (!user) throw new CredentialsSignin()\n      else user.id = user.id?.toString() ?? crypto.randomUUID()\n\n      const account = {\n        providerAccountId: user.id,\n        type: \"credentials\",\n        provider: provider.id,\n      } satisfies Account\n\n      const redirect = await handleAuthorized(\n        { user, account, credentials },\n        options\n      )\n      if (redirect) return { redirect, cookies }\n\n      const defaultToken = {\n        name: user.name,\n        email: user.email,\n        picture: user.image,\n        sub: user.id,\n      }\n\n      const token = await callbacks.jwt({\n        token: defaultToken,\n        user,\n        account,\n        isNewUser: false,\n        trigger: \"signIn\",\n      })\n\n      // Clear cookies if token is null\n      if (token === null) {\n        cookies.push(...sessionStore.clean())\n      } else {\n        const salt = options.cookies.sessionToken.name\n        // Encode token\n        const newToken = await jwt.encode({ ...jwt, token, salt })\n\n        // Set cookie expiry date\n        const cookieExpires = new Date()\n        cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)\n\n        const sessionCookies = sessionStore.chunk(newToken, {\n          expires: cookieExpires,\n        })\n\n        cookies.push(...sessionCookies)\n      }\n\n      await events.signIn?.({ user, account })\n\n      return { redirect: callbackUrl, cookies }\n    } else if (provider.type === \"webauthn\" && method === \"POST\") {\n      // Get callback action from request. It should be either \"authenticate\" or \"register\"\n      const action = request.body?.action\n      if (\n        typeof action !== \"string\" ||\n        (action !== \"authenticate\" && action !== \"register\")\n      ) {\n        throw new AuthError(\"Invalid action parameter\")\n      }\n      // Return an error if the adapter is missing or if the provider\n      // is not a webauthn provider.\n      const localOptions = assertInternalOptionsWebAuthn(options)\n\n      // Verify request to get user, account and authenticator\n      let user: User\n      let account: Account\n      let authenticator: Authenticator | undefined\n      switch (action) {\n        case \"authenticate\": {\n          const verified = await verifyAuthenticate(\n            localOptions,\n            request,\n            cookies\n          )\n\n          user = verified.user\n          account = verified.account\n\n          break\n        }\n        case \"register\": {\n          const verified = await verifyRegister(options, request, cookies)\n\n          user = verified.user\n          account = verified.account\n          authenticator = verified.authenticator\n\n          break\n        }\n      }\n\n      // Check if user is allowed to sign in\n      await handleAuthorized({ user, account }, options)\n\n      // Sign user in, creating them and their account if needed\n      const {\n        user: loggedInUser,\n        isNewUser,\n        session,\n        account: currentAccount,\n      } = await handleLoginOrRegister(\n        sessionStore.value,\n        user,\n        account,\n        options\n      )\n\n      if (!currentAccount) {\n        // This is mostly for type checking. It should never actually happen.\n        throw new AuthError(\"Error creating or finding account\")\n      }\n\n      // Create new authenticator if needed\n      if (authenticator && loggedInUser.id) {\n        await localOptions.adapter.createAuthenticator({\n          ...authenticator,\n          userId: loggedInUser.id,\n        })\n      }\n\n      // Do the session registering dance\n      if (useJwtSession) {\n        const defaultToken = {\n          name: loggedInUser.name,\n          email: loggedInUser.email,\n          picture: loggedInUser.image,\n          sub: loggedInUser.id?.toString(),\n        }\n        const token = await callbacks.jwt({\n          token: defaultToken,\n          user: loggedInUser,\n          account: currentAccount,\n          isNewUser,\n          trigger: isNewUser ? \"signUp\" : \"signIn\",\n        })\n\n        // Clear cookies if token is null\n        if (token === null) {\n          cookies.push(...sessionStore.clean())\n        } else {\n          const salt = options.cookies.sessionToken.name\n          // Encode token\n          const newToken = await jwt.encode({ ...jwt, token, salt })\n\n          // Set cookie expiry date\n          const cookieExpires = new Date()\n          cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)\n\n          const sessionCookies = sessionStore.chunk(newToken, {\n            expires: cookieExpires,\n          })\n          cookies.push(...sessionCookies)\n        }\n      } else {\n        // Save Session Token in cookie\n        cookies.push({\n          name: options.cookies.sessionToken.name,\n          value: (session as AdapterSession).sessionToken,\n          options: {\n            ...options.cookies.sessionToken.options,\n            expires: (session as AdapterSession).expires,\n          },\n        })\n      }\n\n      await events.signIn?.({\n        user: loggedInUser,\n        account: currentAccount,\n        isNewUser,\n      })\n\n      // Handle first logins on new accounts\n      // e.g. option to send users to a new account landing page on initial login\n      // Note that the callback URL is preserved, so the journey can still be resumed\n      if (isNewUser && pages.newUser) {\n        return {\n          redirect: `${pages.newUser}${\n            pages.newUser.includes(\"?\") ? \"&\" : \"?\"\n          }${new URLSearchParams({ callbackUrl })}`,\n          cookies,\n        }\n      }\n\n      // Callback URL is already verified at this point, so safe to use if specified\n      return { redirect: callbackUrl, cookies }\n    }\n\n    throw new InvalidProvider(\n      `Callback for provider type (${provider.type}) is not supported`\n    )\n  } catch (e) {\n    if (e instanceof AuthError) throw e\n    const error = new CallbackRouteError(e as Error, { provider: provider.id })\n    logger.debug(\"callback route error details\", { method, query, body })\n    throw error\n  }\n}\n\nasync function handleAuthorized(\n  params: Parameters<InternalOptions[\"callbacks\"][\"signIn\"]>[0],\n  config: InternalOptions\n): Promise<string | undefined> {\n  let authorized\n  const { signIn, redirect } = config.callbacks\n  try {\n    authorized = await signIn(params)\n  } catch (e) {\n    if (e instanceof AuthError) throw e\n    throw new AccessDenied(e as Error)\n  }\n  if (!authorized) throw new AccessDenied(\"AccessDenied\")\n  if (typeof authorized !== \"string\") return\n  return await redirect({ url: authorized, baseUrl: config.url.origin })\n}\n"
  },
  {
    "path": "packages/core/src/lib/actions/callback/oauth/callback.ts",
    "content": "import * as checks from \"./checks.js\"\nimport * as o from \"oauth4webapi\"\nimport {\n  OAuthCallbackError,\n  OAuthProfileParseError,\n} from \"../../../../errors.js\"\n\nimport type {\n  Account,\n  InternalOptions,\n  LoggerInstance,\n  Profile,\n  RequestInternal,\n  TokenSet,\n  User,\n} from \"../../../../types.js\"\nimport { type OAuthConfigInternal } from \"../../../../providers/index.js\"\nimport type { Cookie } from \"../../../utils/cookie.js\"\nimport { isOIDCProvider } from \"../../../utils/providers.js\"\nimport { conformInternal, customFetch } from \"../../../symbols.js\"\nimport { decodeJwt } from \"jose\"\n\nfunction formUrlEncode(token: string) {\n  return encodeURIComponent(token).replace(/%20/g, \"+\")\n}\n\n/**\n * Formats client_id and client_secret as an HTTP Basic Authentication header as per the OAuth 2.0\n * specified in RFC6749.\n */\nfunction clientSecretBasic(clientId: string, clientSecret: string) {\n  const username = formUrlEncode(clientId)\n  const password = formUrlEncode(clientSecret)\n  const credentials = btoa(`${username}:${password}`)\n  return `Basic ${credentials}`\n}\n\n/**\n * Handles the following OAuth steps.\n * https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1\n * https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3\n * https://openid.net/specs/openid-connect-core-1_0.html#UserInfoRequest\n *\n * @note Although requesting userinfo is not required by the OAuth2.0 spec,\n * we fetch it anyway. This is because we always want a user profile.\n */\nexport async function handleOAuth(\n  params: RequestInternal[\"query\"],\n  cookies: RequestInternal[\"cookies\"],\n  options: InternalOptions<\"oauth\" | \"oidc\">\n) {\n  const { logger, provider } = options\n\n  let as: o.AuthorizationServer\n\n  const { token, userinfo } = provider\n  // Falls back to authjs.dev if the user only passed params\n  if (\n    (!token?.url || token.url.host === \"authjs.dev\") &&\n    (!userinfo?.url || userinfo.url.host === \"authjs.dev\")\n  ) {\n    // We assume that issuer is always defined as this has been asserted earlier\n\n    const issuer = new URL(provider.issuer!)\n    const discoveryResponse = await o.discoveryRequest(issuer, {\n      [o.allowInsecureRequests]: true,\n      [o.customFetch]: provider[customFetch],\n    })\n    as = await o.processDiscoveryResponse(issuer, discoveryResponse)\n\n    if (!as.token_endpoint)\n      throw new TypeError(\n        \"TODO: Authorization server did not provide a token endpoint.\"\n      )\n\n    if (!as.userinfo_endpoint)\n      throw new TypeError(\n        \"TODO: Authorization server did not provide a userinfo endpoint.\"\n      )\n  } else {\n    as = {\n      issuer: provider.issuer ?? \"https://authjs.dev\", // TODO: review fallback issuer\n      token_endpoint: token?.url.toString(),\n      userinfo_endpoint: userinfo?.url.toString(),\n    }\n  }\n\n  const client: o.Client = {\n    client_id: provider.clientId,\n    ...provider.client,\n  }\n\n  let clientAuth: o.ClientAuth\n\n  switch (client.token_endpoint_auth_method) {\n    // TODO: in the next breaking major version have undefined be `client_secret_post`\n    case undefined:\n    case \"client_secret_basic\":\n      // TODO: in the next breaking major version use o.ClientSecretBasic() here\n      clientAuth = (_as, _client, _body, headers) => {\n        headers.set(\n          \"authorization\",\n          clientSecretBasic(provider.clientId, provider.clientSecret!)\n        )\n      }\n      break\n    case \"client_secret_post\":\n      clientAuth = o.ClientSecretPost(provider.clientSecret!)\n      break\n    case \"client_secret_jwt\":\n      clientAuth = o.ClientSecretJwt(provider.clientSecret!)\n      break\n    case \"private_key_jwt\":\n      clientAuth = o.PrivateKeyJwt(provider.token!.clientPrivateKey!, {\n        // TODO: review in the next breaking change\n        [o.modifyAssertion](_header, payload) {\n          payload.aud = [as.issuer, as.token_endpoint!]\n        },\n      })\n      break\n    case \"none\":\n      clientAuth = o.None()\n      break\n    default:\n      throw new Error(\"unsupported client authentication method\")\n  }\n\n  const resCookies: Cookie[] = []\n\n  const state = await checks.state.use(cookies, resCookies, options)\n\n  let codeGrantParams: URLSearchParams\n  try {\n    codeGrantParams = o.validateAuthResponse(\n      as,\n      client,\n      new URLSearchParams(params),\n      provider.checks.includes(\"state\") ? state : o.skipStateCheck\n    )\n  } catch (err) {\n    if (err instanceof o.AuthorizationResponseError) {\n      const cause = {\n        providerId: provider.id,\n        ...Object.fromEntries(err.cause.entries()),\n      }\n      logger.debug(\"OAuthCallbackError\", cause)\n      throw new OAuthCallbackError(\"OAuth Provider returned an error\", cause)\n    }\n    throw err\n  }\n\n  const codeVerifier = await checks.pkce.use(cookies, resCookies, options)\n\n  let redirect_uri = provider.callbackUrl\n  if (!options.isOnRedirectProxy && provider.redirectProxyUrl) {\n    redirect_uri = provider.redirectProxyUrl\n  }\n\n  let codeGrantResponse = await o.authorizationCodeGrantRequest(\n    as,\n    client,\n    clientAuth,\n    codeGrantParams,\n    redirect_uri,\n    codeVerifier ?? \"decoy\",\n    {\n      // TODO: move away from allowing insecure HTTP requests\n      [o.allowInsecureRequests]: true,\n      [o.customFetch]: (...args) => {\n        if (!provider.checks.includes(\"pkce\")) {\n          args[1].body.delete(\"code_verifier\")\n        }\n        return (provider[customFetch] ?? fetch)(...args)\n      },\n    }\n  )\n\n  if (provider.token?.conform) {\n    codeGrantResponse =\n      (await provider.token.conform(codeGrantResponse.clone())) ??\n      codeGrantResponse\n  }\n\n  let profile: Profile = {}\n\n  const requireIdToken = isOIDCProvider(provider)\n\n  if (provider[conformInternal]) {\n    switch (provider.id) {\n      case \"microsoft-entra-id\":\n      case \"azure-ad\": {\n        /**\n         * These providers return errors in the response body and\n         * need the authorization server metadata to be re-processed\n         * based on the `id_token`'s `tid` claim.\n         * @see: https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow#error-response-1\n         */\n        const responseJson = await codeGrantResponse.clone().json()\n        if (responseJson.error) {\n          const cause = {\n            providerId: provider.id,\n            ...responseJson,\n          }\n          throw new OAuthCallbackError(\n            `OAuth Provider returned an error: ${responseJson.error}`,\n            cause\n          )\n        }\n        const { tid } = decodeJwt(responseJson.id_token)\n        if (typeof tid === \"string\") {\n          const tenantRe = /microsoftonline\\.com\\/(\\w+)\\/v2\\.0/\n          const tenantId = as.issuer?.match(tenantRe)?.[1] ?? \"common\"\n          const issuer = new URL(as.issuer.replace(tenantId, tid))\n          const discoveryResponse = await o.discoveryRequest(issuer, {\n            [o.customFetch]: provider[customFetch],\n          })\n          as = await o.processDiscoveryResponse(issuer, discoveryResponse)\n        }\n        break\n      }\n      default:\n        break\n    }\n  }\n  const processedCodeResponse = await o.processAuthorizationCodeResponse(\n    as,\n    client,\n    codeGrantResponse,\n    {\n      expectedNonce: await checks.nonce.use(cookies, resCookies, options),\n      requireIdToken,\n    }\n  )\n\n  const tokens: TokenSet & Pick<Account, \"expires_at\"> = processedCodeResponse\n\n  if (requireIdToken) {\n    const idTokenClaims = o.getValidatedIdTokenClaims(processedCodeResponse)!\n    profile = idTokenClaims\n\n    // Apple sends some of the user information in a `user` parameter as a stringified JSON.\n    // It also only does so the first time the user consents to share their information.\n    if (provider[conformInternal] && provider.id === \"apple\") {\n      try {\n        profile.user = JSON.parse(params?.user)\n      } catch {}\n    }\n\n    if (provider.idToken === false) {\n      const userinfoResponse = await o.userInfoRequest(\n        as,\n        client,\n        processedCodeResponse.access_token,\n        {\n          [o.customFetch]: provider[customFetch],\n          // TODO: move away from allowing insecure HTTP requests\n          [o.allowInsecureRequests]: true,\n        }\n      )\n\n      profile = await o.processUserInfoResponse(\n        as,\n        client,\n        idTokenClaims.sub,\n        userinfoResponse\n      )\n    }\n  } else {\n    if (userinfo?.request) {\n      const _profile = await userinfo.request({ tokens, provider })\n      if (_profile instanceof Object) profile = _profile\n    } else if (userinfo?.url) {\n      const userinfoResponse = await o.userInfoRequest(\n        as,\n        client,\n        processedCodeResponse.access_token,\n        {\n          [o.customFetch]: provider[customFetch],\n          // TODO: move away from allowing insecure HTTP requests\n          [o.allowInsecureRequests]: true,\n        }\n      )\n      profile = await userinfoResponse.json()\n    } else {\n      throw new TypeError(\"No userinfo endpoint configured\")\n    }\n  }\n\n  if (tokens.expires_in) {\n    tokens.expires_at =\n      Math.floor(Date.now() / 1000) + Number(tokens.expires_in)\n  }\n\n  const profileResult = await getUserAndAccount(\n    profile,\n    provider,\n    tokens,\n    logger\n  )\n\n  return { ...profileResult, profile, cookies: resCookies }\n}\n\n/**\n * Returns the user and account that is going to be created in the database.\n * @internal\n */\nexport async function getUserAndAccount(\n  OAuthProfile: Profile,\n  provider: OAuthConfigInternal<any>,\n  tokens: TokenSet,\n  logger: LoggerInstance\n) {\n  try {\n    const userFromProfile = await provider.profile(OAuthProfile, tokens)\n    const user = {\n      ...userFromProfile,\n      // The user's id is intentionally not set based on the profile id, as\n      // the user should remain independent of the provider and the profile id\n      // is saved on the Account already, as `providerAccountId`.\n      id: crypto.randomUUID(),\n      email: userFromProfile.email?.toLowerCase(),\n    } satisfies User\n\n    return {\n      user,\n      account: {\n        ...tokens,\n        provider: provider.id,\n        type: provider.type,\n        providerAccountId: userFromProfile.id ?? crypto.randomUUID(),\n      },\n    }\n  } catch (e) {\n    // If we didn't get a response either there was a problem with the provider\n    // response *or* the user cancelled the action with the provider.\n    //\n    // Unfortunately, we can't tell which - at least not in a way that works for\n    // all providers, so we return an empty object; the user should then be\n    // redirected back to the sign up page. We log the error to help developers\n    // who might be trying to debug this when configuring a new provider.\n    logger.debug(\"getProfile error details\", OAuthProfile)\n    logger.error(\n      new OAuthProfileParseError(e as Error, { provider: provider.id })\n    )\n  }\n}\n"
  },
  {
    "path": "packages/core/src/lib/actions/callback/oauth/checks.ts",
    "content": "import * as o from \"oauth4webapi\"\nimport { InvalidCheck } from \"../../../../errors.js\"\n\n// NOTE: We use the default JWT methods here because they encrypt/decrypt the payload, not just sign it.\nimport { decode, encode } from \"../../../../jwt.js\"\n\nimport type {\n  CookiesOptions,\n  InternalOptions,\n  RequestInternal,\n  User,\n} from \"../../../../types.js\"\nimport type { Cookie } from \"../../../utils/cookie.js\"\nimport type { WebAuthnProviderType } from \"../../../../providers/webauthn.js\"\n\ninterface CookiePayload {\n  value: string\n}\n\nconst COOKIE_TTL = 60 * 15 // 15 minutes\n\n/** Returns a cookie with a JWT encrypted payload. */\nasync function sealCookie(\n  name: keyof CookiesOptions,\n  payload: string,\n  options: InternalOptions<\"oauth\" | \"oidc\" | WebAuthnProviderType>\n): Promise<Cookie> {\n  const { cookies, logger } = options\n  const cookie = cookies[name]\n  const expires = new Date()\n  expires.setTime(expires.getTime() + COOKIE_TTL * 1000)\n\n  logger.debug(`CREATE_${name.toUpperCase()}`, {\n    name: cookie.name,\n    payload,\n    COOKIE_TTL,\n    expires,\n  })\n\n  const encoded = await encode({\n    ...options.jwt,\n    maxAge: COOKIE_TTL,\n    token: { value: payload } satisfies CookiePayload,\n    salt: cookie.name,\n  })\n  const cookieOptions = { ...cookie.options, expires }\n  return { name: cookie.name, value: encoded, options: cookieOptions }\n}\n\nasync function parseCookie(\n  name: keyof CookiesOptions,\n  value: string | undefined,\n  options: InternalOptions\n): Promise<string> {\n  try {\n    const { logger, cookies, jwt } = options\n    logger.debug(`PARSE_${name.toUpperCase()}`, { cookie: value })\n\n    if (!value) throw new InvalidCheck(`${name} cookie was missing`)\n    const parsed = await decode<CookiePayload>({\n      ...jwt,\n      token: value,\n      salt: cookies[name].name,\n    })\n    if (parsed?.value) return parsed.value\n    throw new Error(\"Invalid cookie\")\n  } catch (error) {\n    throw new InvalidCheck(`${name} value could not be parsed`, {\n      cause: error,\n    })\n  }\n}\n\nfunction clearCookie(\n  name: keyof CookiesOptions,\n  options: InternalOptions,\n  resCookies: Cookie[]\n) {\n  const { logger, cookies } = options\n  const cookie = cookies[name]\n  logger.debug(`CLEAR_${name.toUpperCase()}`, { cookie })\n  resCookies.push({\n    name: cookie.name,\n    value: \"\",\n    options: { ...cookies[name].options, maxAge: 0 },\n  })\n}\n\nfunction useCookie(\n  check: \"state\" | \"pkce\" | \"nonce\",\n  name: keyof CookiesOptions\n) {\n  return async function (\n    cookies: RequestInternal[\"cookies\"],\n    resCookies: Cookie[],\n    options: InternalOptions<\"oidc\">\n  ) {\n    const { provider, logger } = options\n    if (!provider?.checks?.includes(check)) return\n    const cookieValue = cookies?.[options.cookies[name].name]\n    logger.debug(`USE_${name.toUpperCase()}`, { value: cookieValue })\n    const parsed = await parseCookie(name, cookieValue, options)\n    clearCookie(name, options, resCookies)\n    return parsed\n  }\n}\n\n/**\n * @see https://www.rfc-editor.org/rfc/rfc7636\n * @see https://danielfett.de/2020/05/16/pkce-vs-nonce-equivalent-or-not/#pkce\n */\nexport const pkce = {\n  /** Creates a PKCE code challenge and verifier pair. The verifier in stored in the cookie. */\n  async create(options: InternalOptions<\"oauth\">) {\n    const code_verifier = o.generateRandomCodeVerifier()\n    const value = await o.calculatePKCECodeChallenge(code_verifier)\n    const cookie = await sealCookie(\"pkceCodeVerifier\", code_verifier, options)\n    return { cookie, value }\n  },\n  /**\n   * Returns code_verifier if the provider is configured to use PKCE,\n   * and clears the container cookie afterwards.\n   * An error is thrown if the code_verifier is missing or invalid.\n   */\n  use: useCookie(\"pkce\", \"pkceCodeVerifier\"),\n}\n\ninterface EncodedState {\n  origin?: string\n  random: string\n}\n\nconst STATE_MAX_AGE = 60 * 15 // 15 minutes in seconds\nconst encodedStateSalt = \"encodedState\"\n\n/**\n * @see https://www.rfc-editor.org/rfc/rfc6749#section-10.12\n * @see https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1\n */\nexport const state = {\n  /** Creates a state cookie with an optionally encoded body. */\n  async create(options: InternalOptions<\"oauth\">, origin?: string) {\n    const { provider } = options\n    if (!provider.checks.includes(\"state\")) {\n      if (origin) {\n        throw new InvalidCheck(\n          \"State data was provided but the provider is not configured to use state\"\n        )\n      }\n      return\n    }\n\n    // IDEA: Allow the user to pass data to be stored in the state\n    const payload = {\n      origin,\n      random: o.generateRandomState(),\n    } satisfies EncodedState\n    const value = await encode({\n      secret: options.jwt.secret,\n      token: payload,\n      salt: encodedStateSalt,\n      maxAge: STATE_MAX_AGE,\n    })\n    const cookie = await sealCookie(\"state\", value, options)\n\n    return { cookie, value }\n  },\n  /**\n   * Returns state if the provider is configured to use state,\n   * and clears the container cookie afterwards.\n   * An error is thrown if the state is missing or invalid.\n   */\n  use: useCookie(\"state\", \"state\"),\n  /** Decodes the state. If it could not be decoded, it throws an error. */\n  async decode(state: string, options: InternalOptions) {\n    try {\n      options.logger.debug(\"DECODE_STATE\", { state })\n      const payload = await decode<EncodedState>({\n        secret: options.jwt.secret,\n        token: state,\n        salt: encodedStateSalt,\n      })\n      if (payload) return payload\n      throw new Error(\"Invalid state\")\n    } catch (error) {\n      throw new InvalidCheck(\"State could not be decoded\", { cause: error })\n    }\n  },\n}\n\nexport const nonce = {\n  async create(options: InternalOptions<\"oidc\">) {\n    if (!options.provider.checks.includes(\"nonce\")) return\n    const value = o.generateRandomNonce()\n    const cookie = await sealCookie(\"nonce\", value, options)\n    return { cookie, value }\n  },\n  /**\n   * Returns nonce if the provider is configured to use nonce,\n   * and clears the container cookie afterwards.\n   * An error is thrown if the nonce is missing or invalid.\n   * @see https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes\n   * @see https://danielfett.de/2020/05/16/pkce-vs-nonce-equivalent-or-not/#nonce\n   */\n  use: useCookie(\"nonce\", \"nonce\"),\n}\n\nconst WEBAUTHN_CHALLENGE_MAX_AGE = 60 * 15 // 15 minutes in seconds\n\ninterface WebAuthnChallengePayload {\n  challenge: string\n  registerData?: User\n}\n\nconst webauthnChallengeSalt = \"encodedWebauthnChallenge\"\nexport const webauthnChallenge = {\n  async create(\n    options: InternalOptions<WebAuthnProviderType>,\n    challenge: string,\n    registerData?: User\n  ) {\n    return {\n      cookie: await sealCookie(\n        \"webauthnChallenge\",\n        await encode({\n          secret: options.jwt.secret,\n          token: { challenge, registerData } satisfies WebAuthnChallengePayload,\n          salt: webauthnChallengeSalt,\n          maxAge: WEBAUTHN_CHALLENGE_MAX_AGE,\n        }),\n        options\n      ),\n    }\n  },\n  /** Returns WebAuthn challenge if present. */\n  async use(\n    options: InternalOptions<WebAuthnProviderType>,\n    cookies: RequestInternal[\"cookies\"],\n    resCookies: Cookie[]\n  ): Promise<WebAuthnChallengePayload> {\n    const cookieValue = cookies?.[options.cookies.webauthnChallenge.name]\n\n    const parsed = await parseCookie(\"webauthnChallenge\", cookieValue, options)\n\n    const payload = await decode<WebAuthnChallengePayload>({\n      secret: options.jwt.secret,\n      token: parsed,\n      salt: webauthnChallengeSalt,\n    })\n\n    // Clear the WebAuthn challenge cookie after use\n    clearCookie(\"webauthnChallenge\", options, resCookies)\n\n    if (!payload) throw new InvalidCheck(\"WebAuthn challenge was missing\")\n\n    return payload\n  },\n}\n"
  },
  {
    "path": "packages/core/src/lib/actions/callback/oauth/csrf-token.ts",
    "content": "import { createHash, randomString } from \"../../../utils/web.js\"\n\nimport type { AuthAction, InternalOptions } from \"../../../../types.js\"\nimport { MissingCSRF } from \"../../../../errors.js\"\ninterface CreateCSRFTokenParams {\n  options: InternalOptions\n  cookieValue?: string\n  isPost: boolean\n  bodyValue?: string\n}\n\n/**\n * Ensure CSRF Token cookie is set for any subsequent requests.\n * Used as part of the strategy for mitigation for CSRF tokens.\n *\n * Creates a cookie like 'next-auth.csrf-token' with the value 'token|hash',\n * where 'token' is the CSRF token and 'hash' is a hash made of the token and\n * the secret, and the two values are joined by a pipe '|'. By storing the\n * value and the hash of the value (with the secret used as a salt) we can\n * verify the cookie was set by the server and not by a malicious attacker.\n *\n * For more details, see the following OWASP links:\n * https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie\n * https://owasp.org/www-chapter-london/assets/slides/David_Johansson-Double_Defeat_of_Double-Submit_Cookie.pdf\n */\nexport async function createCSRFToken({\n  options,\n  cookieValue,\n  isPost,\n  bodyValue,\n}: CreateCSRFTokenParams) {\n  if (cookieValue) {\n    const [csrfToken, csrfTokenHash] = cookieValue.split(\"|\")\n\n    const expectedCsrfTokenHash = await createHash(\n      `${csrfToken}${options.secret}`\n    )\n\n    if (csrfTokenHash === expectedCsrfTokenHash) {\n      // If hash matches then we trust the CSRF token value\n      // If this is a POST request and the CSRF Token in the POST request matches\n      // the cookie we have already verified is the one we have set, then the token is verified!\n      const csrfTokenVerified = isPost && csrfToken === bodyValue\n\n      return { csrfTokenVerified, csrfToken }\n    }\n  }\n\n  // New CSRF token\n  const csrfToken = randomString(32)\n  const csrfTokenHash = await createHash(`${csrfToken}${options.secret}`)\n  const cookie = `${csrfToken}|${csrfTokenHash}`\n\n  return { cookie, csrfToken }\n}\n\nexport function validateCSRF(action: AuthAction, verified?: boolean) {\n  if (verified) return\n  throw new MissingCSRF(`CSRF token was missing during an action ${action}`)\n}\n"
  },
  {
    "path": "packages/core/src/lib/actions/index.ts",
    "content": "export { callback } from \"./callback/index.js\"\nexport { session } from \"./session.js\"\nexport { signIn } from \"./signin/index.js\"\nexport { signOut } from \"./signout.js\"\nexport { webAuthnOptions } from \"./webauthn-options.js\"\n"
  },
  {
    "path": "packages/core/src/lib/actions/session.ts",
    "content": "import { JWTSessionError, SessionTokenError } from \"../../errors.js\"\nimport { fromDate } from \"../utils/date.js\"\n\nimport type { Adapter } from \"../../adapters.js\"\nimport type { InternalOptions, ResponseInternal, Session } from \"../../types.js\"\nimport type { Cookie, SessionStore } from \"../utils/cookie.js\"\n\n/** Return a session object filtered via `callbacks.session` */\nexport async function session(\n  options: InternalOptions,\n  sessionStore: SessionStore,\n  cookies: Cookie[],\n  isUpdate?: boolean,\n  newSession?: any\n): Promise<ResponseInternal<Session | null>> {\n  const {\n    adapter,\n    jwt,\n    events,\n    callbacks,\n    logger,\n    session: { strategy: sessionStrategy, maxAge: sessionMaxAge },\n  } = options\n\n  const response: ResponseInternal<Session | null> = {\n    body: null,\n    headers: {\n      \"Content-Type\": \"application/json\",\n      ...(!isUpdate && {\n        \"Cache-Control\": \"private, no-cache, no-store\",\n        Expires: \"0\",\n        Pragma: \"no-cache\",\n      }),\n    },\n    cookies,\n  }\n\n  const sessionToken = sessionStore.value\n\n  if (!sessionToken) return response\n\n  if (sessionStrategy === \"jwt\") {\n    try {\n      const salt = options.cookies.sessionToken.name\n      const payload = await jwt.decode({ ...jwt, token: sessionToken, salt })\n\n      if (!payload) throw new Error(\"Invalid JWT\")\n\n      // @ts-expect-error\n      const token = await callbacks.jwt({\n        token: payload,\n        ...(isUpdate && { trigger: \"update\" }),\n        session: newSession,\n      })\n\n      const newExpires = fromDate(sessionMaxAge)\n\n      if (token !== null) {\n        // By default, only exposes a limited subset of information to the client\n        // as needed for presentation purposes (e.g. \"you are logged in as...\").\n        const session = {\n          user: { name: token.name, email: token.email, image: token.picture },\n          expires: newExpires.toISOString(),\n        }\n        // @ts-expect-error\n        const newSession = await callbacks.session({ session, token })\n\n        // Return session payload as response\n        response.body = newSession\n\n        // Refresh JWT expiry by re-signing it, with an updated expiry date\n        const newToken = await jwt.encode({ ...jwt, token, salt })\n\n        // Set cookie, to also update expiry date on cookie\n        const sessionCookies = sessionStore.chunk(newToken, {\n          expires: newExpires,\n        })\n\n        response.cookies?.push(...sessionCookies)\n\n        await events.session?.({ session: newSession, token })\n      } else {\n        response.cookies?.push(...sessionStore.clean())\n      }\n    } catch (e) {\n      logger.error(new JWTSessionError(e as Error))\n      // If the JWT is not verifiable remove the broken session cookie(s).\n      response.cookies?.push(...sessionStore.clean())\n    }\n\n    return response\n  }\n\n  // Retrieve session from database\n  try {\n    const { getSessionAndUser, deleteSession, updateSession } =\n      adapter as Required<Adapter>\n    let userAndSession = await getSessionAndUser(sessionToken)\n\n    // If session has expired, clean up the database\n    if (\n      userAndSession &&\n      userAndSession.session.expires.valueOf() < Date.now()\n    ) {\n      await deleteSession(sessionToken)\n      userAndSession = null\n    }\n\n    if (userAndSession) {\n      const { user, session } = userAndSession\n\n      const sessionUpdateAge = options.session.updateAge\n      // Calculate last updated date to throttle write updates to database\n      // Formula: ({expiry date} - sessionMaxAge) + sessionUpdateAge\n      //     e.g. ({expiry date} - 30 days) + 1 hour\n      const sessionIsDueToBeUpdatedDate =\n        session.expires.valueOf() -\n        sessionMaxAge * 1000 +\n        sessionUpdateAge * 1000\n\n      const newExpires = fromDate(sessionMaxAge)\n      // Trigger update of session expiry date and write to database, only\n      // if the session was last updated more than {sessionUpdateAge} ago\n      if (sessionIsDueToBeUpdatedDate <= Date.now()) {\n        await updateSession({\n          sessionToken: sessionToken,\n          expires: newExpires,\n        })\n      }\n\n      // Pass Session through to the session callback\n      const sessionPayload = await callbacks.session({\n        // TODO: user already passed below,\n        // remove from session object in https://github.com/nextauthjs/next-auth/pull/9702\n        // @ts-expect-error\n        session: { ...session, user },\n        user,\n        newSession,\n        ...(isUpdate ? { trigger: \"update\" } : {}),\n      })\n\n      // Return session payload as response\n      response.body = sessionPayload\n\n      // Set cookie again to update expiry\n      response.cookies?.push({\n        name: options.cookies.sessionToken.name,\n        value: sessionToken,\n        options: {\n          ...options.cookies.sessionToken.options,\n          expires: newExpires,\n        },\n      })\n\n      // @ts-expect-error\n      await events.session?.({ session: sessionPayload })\n    } else if (sessionToken) {\n      // If `sessionToken` was found set but it's not valid for a session then\n      // remove the sessionToken cookie from browser.\n      response.cookies?.push(...sessionStore.clean())\n    }\n  } catch (e) {\n    logger.error(new SessionTokenError(e as Error))\n  }\n\n  return response\n}\n"
  },
  {
    "path": "packages/core/src/lib/actions/signin/authorization-url.ts",
    "content": "import * as checks from \"../callback/oauth/checks.js\"\nimport * as o from \"oauth4webapi\"\n\nimport type { InternalOptions, RequestInternal } from \"../../../types.js\"\nimport type { Cookie } from \"../../utils/cookie.js\"\nimport { customFetch } from \"../../symbols.js\"\n\n/**\n * Generates an authorization/request token URL.\n *\n * [OAuth 2](https://www.oauth.com/oauth2-servers/authorization/the-authorization-request/)\n */\nexport async function getAuthorizationUrl(\n  query: RequestInternal[\"query\"],\n  options: InternalOptions<\"oauth\" | \"oidc\">\n) {\n  const { logger, provider } = options\n\n  let url = provider.authorization?.url\n  let as: o.AuthorizationServer | undefined\n\n  // Falls back to authjs.dev if the user only passed params\n  if (!url || url.host === \"authjs.dev\") {\n    // If url is undefined, we assume that issuer is always defined\n    // We check this in assert.ts\n\n    const issuer = new URL(provider.issuer!)\n    const discoveryResponse = await o.discoveryRequest(issuer, {\n      [o.customFetch]: provider[customFetch],\n      // TODO: move away from allowing insecure HTTP requests\n      [o.allowInsecureRequests]: true,\n    })\n    const as = await o\n      .processDiscoveryResponse(issuer, discoveryResponse)\n      .catch((error) => {\n        if (!(error instanceof TypeError) || error.message !== \"Invalid URL\")\n          throw error\n        throw new TypeError(\n          `Discovery request responded with an invalid issuer. expected: ${issuer}`\n        )\n      })\n\n    if (!as.authorization_endpoint) {\n      throw new TypeError(\n        \"Authorization server did not provide an authorization endpoint.\"\n      )\n    }\n\n    url = new URL(as.authorization_endpoint)\n  }\n\n  const authParams = url.searchParams\n\n  let redirect_uri: string = provider.callbackUrl\n  let data: string | undefined\n  if (!options.isOnRedirectProxy && provider.redirectProxyUrl) {\n    redirect_uri = provider.redirectProxyUrl\n    data = provider.callbackUrl\n    logger.debug(\"using redirect proxy\", { redirect_uri, data })\n  }\n\n  const params = Object.assign(\n    {\n      response_type: \"code\",\n      // clientId can technically be undefined, should we check this in assert.ts or rely on the Authorization Server to do it?\n      client_id: provider.clientId,\n      redirect_uri,\n      // @ts-expect-error TODO:\n      ...provider.authorization?.params,\n    },\n    Object.fromEntries(provider.authorization?.url.searchParams ?? []),\n    query\n  )\n\n  for (const k in params) authParams.set(k, params[k])\n\n  const cookies: Cookie[] = []\n\n  if (\n    // Otherwise \"POST /redirect_uri\" wouldn't include the cookies\n    provider.authorization?.url.searchParams.get(\"response_mode\") ===\n    \"form_post\"\n  ) {\n    options.cookies.state.options.sameSite = \"none\"\n    options.cookies.state.options.secure = true\n    options.cookies.nonce.options.sameSite = \"none\"\n    options.cookies.nonce.options.secure = true\n  }\n\n  const state = await checks.state.create(options, data)\n  if (state) {\n    authParams.set(\"state\", state.value)\n    cookies.push(state.cookie)\n  }\n\n  if (provider.checks?.includes(\"pkce\")) {\n    if (as && !as.code_challenge_methods_supported?.includes(\"S256\")) {\n      // We assume S256 PKCE support, if the server does not advertise that,\n      // a random `nonce` must be used for CSRF protection.\n      if (provider.type === \"oidc\") provider.checks = [\"nonce\"]\n    } else {\n      const { value, cookie } = await checks.pkce.create(options)\n      authParams.set(\"code_challenge\", value)\n      authParams.set(\"code_challenge_method\", \"S256\")\n      cookies.push(cookie)\n    }\n  }\n\n  const nonce = await checks.nonce.create(options)\n  if (nonce) {\n    authParams.set(\"nonce\", nonce.value)\n    cookies.push(nonce.cookie)\n  }\n\n  // TODO: This does not work in normalizeOAuth because authorization endpoint can come from discovery\n  // Need to make normalizeOAuth async\n  if (provider.type === \"oidc\" && !url.searchParams.has(\"scope\")) {\n    url.searchParams.set(\"scope\", \"openid profile email\")\n  }\n\n  logger.debug(\"authorization url is ready\", { url, cookies, provider })\n  return { redirect: url.toString(), cookies }\n}\n"
  },
  {
    "path": "packages/core/src/lib/actions/signin/index.ts",
    "content": "import { getAuthorizationUrl } from \"./authorization-url.js\"\nimport { sendToken } from \"./send-token.js\"\n\nimport type { Cookie } from \"../../utils/cookie.js\"\nimport type {\n  InternalOptions,\n  RequestInternal,\n  ResponseInternal,\n} from \"../../../types.js\"\n\nexport async function signIn(\n  request: RequestInternal,\n  cookies: Cookie[],\n  options: InternalOptions\n): Promise<ResponseInternal> {\n  const signInUrl = `${options.url.origin}${options.basePath}/signin`\n\n  if (!options.provider) return { redirect: signInUrl, cookies }\n\n  switch (options.provider.type) {\n    case \"oauth\":\n    case \"oidc\": {\n      const { redirect, cookies: authCookies } = await getAuthorizationUrl(\n        request.query,\n        options\n      )\n      if (authCookies) cookies.push(...authCookies)\n      return { redirect, cookies }\n    }\n    case \"email\": {\n      const response = await sendToken(request, options)\n      return { ...response, cookies }\n    }\n    default:\n      return { redirect: signInUrl, cookies }\n  }\n}\n"
  },
  {
    "path": "packages/core/src/lib/actions/signin/send-token.ts",
    "content": "import { createHash, randomString, toRequest } from \"../../utils/web.js\"\nimport { AccessDenied } from \"../../../errors.js\"\n\nimport type { InternalOptions, RequestInternal } from \"../../../types.js\"\nimport type { Account } from \"../../../types.js\"\n\n/**\n * Starts an e-mail login flow, by generating a token,\n * and sending it to the user's e-mail (with the help of a DB adapter).\n * At the end, it returns a redirect to the `verify-request` page.\n */\nexport async function sendToken(\n  request: RequestInternal,\n  options: InternalOptions<\"email\">\n) {\n  const { body } = request\n  const { provider, callbacks, adapter } = options\n  const normalizer = provider.normalizeIdentifier ?? defaultNormalizer\n  const email = normalizer(body?.email)\n\n  const defaultUser = { id: crypto.randomUUID(), email, emailVerified: null }\n  const user = (await adapter!.getUserByEmail(email)) ?? defaultUser\n\n  const account = {\n    providerAccountId: email,\n    userId: user.id,\n    type: \"email\",\n    provider: provider.id,\n  } satisfies Account\n\n  let authorized\n  try {\n    authorized = await callbacks.signIn({\n      user,\n      account,\n      email: { verificationRequest: true },\n    })\n  } catch (e) {\n    throw new AccessDenied(e as Error)\n  }\n  if (!authorized) throw new AccessDenied(\"AccessDenied\")\n  if (typeof authorized === \"string\") {\n    return {\n      redirect: await callbacks.redirect({\n        url: authorized,\n        baseUrl: options.url.origin,\n      }),\n    }\n  }\n\n  const { callbackUrl, theme } = options\n  const token =\n    (await provider.generateVerificationToken?.()) ?? randomString(32)\n\n  const ONE_DAY_IN_SECONDS = 86400\n  const expires = new Date(\n    Date.now() + (provider.maxAge ?? ONE_DAY_IN_SECONDS) * 1000\n  )\n\n  const secret = provider.secret ?? options.secret\n\n  const baseUrl = new URL(options.basePath, options.url.origin)\n\n  const sendRequest = provider.sendVerificationRequest({\n    identifier: email,\n    token,\n    expires,\n    url: `${baseUrl}/callback/${provider.id}?${new URLSearchParams({\n      callbackUrl,\n      token,\n      email,\n    })}`,\n    provider,\n    theme,\n    request: toRequest(request),\n  })\n\n  const createToken = adapter!.createVerificationToken?.({\n    identifier: email,\n    token: await createHash(`${token}${secret}`),\n    expires,\n  })\n\n  await Promise.all([sendRequest, createToken])\n\n  return {\n    redirect: `${baseUrl}/verify-request?${new URLSearchParams({\n      provider: provider.id,\n      type: provider.type,\n    })}`,\n  }\n}\n\nfunction defaultNormalizer(email?: string) {\n  if (!email) throw new Error(\"Missing email from request body.\")\n\n  const trimmedEmail = email.toLowerCase().trim()\n\n  // Reject email addresses with quotes to prevent address parser confusion\n  // This prevents attacks like \"attacker@evil.com\"@victim.com\n  if (trimmedEmail.includes('\"')) {\n    throw new Error(\"Invalid email address format.\")\n  }\n\n  // Get the first two elements only,\n  // separated by `@` from user input.\n  let [local, domain] = trimmedEmail.split(\"@\")\n\n  // Validate that we have exactly 2 parts (local and domain)\n  if (!local || !domain || trimmedEmail.split(\"@\").length !== 2) {\n    throw new Error(\"Invalid email address format.\")\n  }\n\n  // The part before \"@\" can contain a \",\"\n  // but we remove it on the domain part\n  domain = domain.split(\",\")[0]\n\n  // Additional validation: domain should not be empty after comma split\n  if (!domain) {\n    throw new Error(\"Invalid email address format.\")\n  }\n\n  return `${local}@${domain}`\n}\n"
  },
  {
    "path": "packages/core/src/lib/actions/signout.ts",
    "content": "import { SignOutError } from \"../../errors.js\"\n\nimport type { InternalOptions, ResponseInternal } from \"../../types.js\"\nimport type { Cookie, SessionStore } from \"../utils/cookie.js\"\n\n/**\n * Destroys the session.\n * If the session strategy is database,\n * The session is also deleted from the database.\n * In any case, the session cookie is cleared and\n * {@link AuthConfig[\"events\"].signOut} is emitted.\n */\nexport async function signOut(\n  cookies: Cookie[],\n  sessionStore: SessionStore,\n  options: InternalOptions\n): Promise<ResponseInternal> {\n  const { jwt, events, callbackUrl: redirect, logger, session } = options\n  const sessionToken = sessionStore.value\n  if (!sessionToken) return { redirect, cookies }\n\n  try {\n    if (session.strategy === \"jwt\") {\n      const salt = options.cookies.sessionToken.name\n      const token = await jwt.decode({ ...jwt, token: sessionToken, salt })\n      await events.signOut?.({ token })\n    } else {\n      const session = await options.adapter?.deleteSession(sessionToken)\n      await events.signOut?.({ session })\n    }\n  } catch (e) {\n    logger.error(new SignOutError(e as Error))\n  }\n\n  cookies.push(...sessionStore.clean())\n\n  return { redirect, cookies }\n}\n"
  },
  {
    "path": "packages/core/src/lib/actions/webauthn-options.ts",
    "content": "import type {\n  InternalOptions,\n  RequestInternal,\n  ResponseInternal,\n  User,\n} from \"../../types.js\"\nimport type { Cookie, SessionStore } from \"../utils/cookie.js\"\nimport { getLoggedInUser } from \"../utils/session.js\"\nimport {\n  assertInternalOptionsWebAuthn,\n  inferWebAuthnOptions,\n  getAuthenticationResponse,\n  getRegistrationResponse,\n} from \"../utils/webauthn-utils.js\"\n\n/**\n * Returns authentication or registration options for a WebAuthn flow\n * depending on the parameters provided.\n */\nexport async function webAuthnOptions(\n  request: RequestInternal,\n  options: InternalOptions,\n  sessionStore: SessionStore,\n  cookies: Cookie[]\n  // @ts-expect-error issue with returning from a switch case\n): Promise<ResponseInternal> {\n  // Return an error if the adapter is missing or if the provider\n  // is not a webauthn provider.\n  const narrowOptions = assertInternalOptionsWebAuthn(options)\n  const { provider } = narrowOptions\n\n  // Extract the action from the query parameters\n  const { action } = (request.query ?? {}) as Record<string, unknown>\n\n  // Action must be either \"register\", \"authenticate\", or undefined\n  if (\n    action !== \"register\" &&\n    action !== \"authenticate\" &&\n    typeof action !== \"undefined\"\n  ) {\n    return {\n      status: 400,\n      body: { error: \"Invalid action\" },\n      cookies,\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n    }\n  }\n\n  // Get the user info from the session\n  const sessionUser = await getLoggedInUser(options, sessionStore)\n\n  // Extract user info from request\n  // If session user exists, we don't need to call getUserInfo\n  const getUserInfoResponse = sessionUser\n    ? {\n        user: sessionUser,\n        exists: true,\n      }\n    : await provider.getUserInfo(options, request)\n\n  const userInfo = getUserInfoResponse?.user\n\n  // Make a decision on what kind of webauthn options to return\n  const decision = inferWebAuthnOptions(\n    action,\n    !!sessionUser,\n    getUserInfoResponse\n  )\n\n  switch (decision) {\n    case \"authenticate\":\n      return getAuthenticationResponse(\n        narrowOptions,\n        request,\n        userInfo,\n        cookies\n      )\n    case \"register\":\n      if (typeof userInfo?.email === \"string\") {\n        return getRegistrationResponse(\n          narrowOptions,\n          request,\n          userInfo as User & { email: string },\n          cookies\n        )\n      }\n      break\n    default:\n      return {\n        status: 400,\n        body: { error: \"Invalid request\" },\n        cookies,\n        headers: {\n          \"Content-Type\": \"application/json\",\n        },\n      }\n  }\n}\n"
  },
  {
    "path": "packages/core/src/lib/index.ts",
    "content": "import { UnknownAction } from \"../errors.js\"\nimport { SessionStore } from \"./utils/cookie.js\"\nimport { init } from \"./init.js\"\nimport renderPage from \"./pages/index.js\"\nimport * as actions from \"./actions/index.js\"\nimport { validateCSRF } from \"./actions/callback/oauth/csrf-token.js\"\n\nimport type { RequestInternal, ResponseInternal } from \"../types.js\"\nimport type { AuthConfig } from \"../index.js\"\nimport { skipCSRFCheck } from \"./symbols.js\"\n\nexport { customFetch, raw, skipCSRFCheck } from \"./symbols.js\"\n\n/** @internal */\nexport async function AuthInternal(\n  request: RequestInternal,\n  authOptions: AuthConfig\n): Promise<ResponseInternal> {\n  const { action, providerId, error, method } = request\n\n  const csrfDisabled = authOptions.skipCSRFCheck === skipCSRFCheck\n\n  const { options, cookies } = await init({\n    authOptions,\n    action,\n    providerId,\n    url: request.url,\n    callbackUrl: request.body?.callbackUrl ?? request.query?.callbackUrl,\n    csrfToken: request.body?.csrfToken,\n    cookies: request.cookies,\n    isPost: method === \"POST\",\n    csrfDisabled,\n  })\n\n  const sessionStore = new SessionStore(\n    options.cookies.sessionToken,\n    request.cookies,\n    options.logger\n  )\n\n  if (method === \"GET\") {\n    const render = renderPage({ ...options, query: request.query, cookies })\n    switch (action) {\n      case \"callback\":\n        return await actions.callback(request, options, sessionStore, cookies)\n      case \"csrf\":\n        return render.csrf(csrfDisabled, options, cookies)\n      case \"error\":\n        return render.error(error)\n      case \"providers\":\n        return render.providers(options.providers)\n      case \"session\":\n        return await actions.session(options, sessionStore, cookies)\n      case \"signin\":\n        return render.signin(providerId, error)\n      case \"signout\":\n        return render.signout()\n      case \"verify-request\":\n        return render.verifyRequest()\n      case \"webauthn-options\":\n        return await actions.webAuthnOptions(\n          request,\n          options,\n          sessionStore,\n          cookies\n        )\n      default:\n    }\n  } else {\n    const { csrfTokenVerified } = options\n    switch (action) {\n      case \"callback\":\n        if (options.provider.type === \"credentials\")\n          // Verified CSRF Token required for credentials providers only\n          validateCSRF(action, csrfTokenVerified)\n        return await actions.callback(request, options, sessionStore, cookies)\n      case \"session\":\n        validateCSRF(action, csrfTokenVerified)\n        return await actions.session(\n          options,\n          sessionStore,\n          cookies,\n          true,\n          request.body?.data\n        )\n      case \"signin\":\n        validateCSRF(action, csrfTokenVerified)\n        return await actions.signIn(request, cookies, options)\n\n      case \"signout\":\n        validateCSRF(action, csrfTokenVerified)\n        return await actions.signOut(cookies, sessionStore, options)\n      default:\n    }\n  }\n  throw new UnknownAction(`Cannot handle action: ${action}`)\n}\n"
  },
  {
    "path": "packages/core/src/lib/init.ts",
    "content": "import * as jwt from \"../jwt.js\"\nimport { createCallbackUrl } from \"./utils/callback-url.js\"\nimport * as cookie from \"./utils/cookie.js\"\nimport { createCSRFToken } from \"./actions/callback/oauth/csrf-token.js\"\n\nimport { AdapterError, EventError } from \"../errors.js\"\nimport parseProviders from \"./utils/providers.js\"\nimport { setLogger, type LoggerInstance } from \"./utils/logger.js\"\nimport { merge } from \"./utils/merge.js\"\n\nimport type { InternalOptions, RequestInternal } from \"../types.js\"\nimport type { AuthConfig } from \"../index.js\"\n\ninterface InitParams {\n  url: URL\n  authOptions: AuthConfig\n  providerId?: string\n  action: InternalOptions[\"action\"]\n  /** Callback URL value extracted from the incoming request. */\n  callbackUrl?: string\n  /** CSRF token value extracted from the incoming request. From body if POST, from query if GET */\n  csrfToken?: string\n  /** Is the incoming request a POST request? */\n  csrfDisabled: boolean\n  isPost: boolean\n  cookies: RequestInternal[\"cookies\"]\n}\n\nexport const defaultCallbacks: InternalOptions[\"callbacks\"] = {\n  signIn() {\n    return true\n  },\n  redirect({ url, baseUrl }) {\n    if (url.startsWith(\"/\")) return `${baseUrl}${url}`\n    else if (new URL(url).origin === baseUrl) return url\n    return baseUrl\n  },\n  session({ session }) {\n    return {\n      user: {\n        name: session.user?.name,\n        email: session.user?.email,\n        image: session.user?.image,\n      },\n      expires: session.expires?.toISOString?.() ?? session.expires,\n    }\n  },\n  jwt({ token }) {\n    return token\n  },\n}\n\n/** Initialize all internal options and cookies. */\nexport async function init({\n  authOptions: config,\n  providerId,\n  action,\n  url,\n  cookies: reqCookies,\n  callbackUrl: reqCallbackUrl,\n  csrfToken: reqCsrfToken,\n  csrfDisabled,\n  isPost,\n}: InitParams): Promise<{\n  options: InternalOptions\n  cookies: cookie.Cookie[]\n}> {\n  const logger = setLogger(config)\n  const { providers, provider } = parseProviders({ url, providerId, config })\n\n  const maxAge = 30 * 24 * 60 * 60 // Sessions expire after 30 days of being idle by default\n\n  let isOnRedirectProxy = false\n  if (\n    (provider?.type === \"oauth\" || provider?.type === \"oidc\") &&\n    provider.redirectProxyUrl\n  ) {\n    try {\n      isOnRedirectProxy =\n        new URL(provider.redirectProxyUrl).origin === url.origin\n    } catch {\n      throw new TypeError(\n        `redirectProxyUrl must be a valid URL. Received: ${provider.redirectProxyUrl}`\n      )\n    }\n  }\n\n  // User provided options are overridden by other options,\n  // except for the options with special handling above\n  const options: InternalOptions = {\n    debug: false,\n    pages: {},\n    theme: {\n      colorScheme: \"auto\",\n      logo: \"\",\n      brandColor: \"\",\n      buttonText: \"\",\n    },\n    // Custom options override defaults\n    ...config,\n    // These computed settings can have values in userOptions but we override them\n    // and are request-specific.\n    url,\n    action,\n    // @ts-expect-errors\n    provider,\n    cookies: merge(\n      cookie.defaultCookies(\n        config.useSecureCookies ?? url.protocol === \"https:\"\n      ),\n      config.cookies\n    ),\n    providers,\n    // Session options\n    session: {\n      // If no adapter specified, force use of JSON Web Tokens (stateless)\n      strategy: config.adapter ? \"database\" : \"jwt\",\n      maxAge,\n      updateAge: 24 * 60 * 60,\n      generateSessionToken: () => crypto.randomUUID(),\n      ...config.session,\n    },\n    // JWT options\n    jwt: {\n      secret: config.secret!, // Asserted in assert.ts\n      maxAge: config.session?.maxAge ?? maxAge, // default to same as `session.maxAge`\n      encode: jwt.encode,\n      decode: jwt.decode,\n      ...config.jwt,\n    },\n    // Event messages\n    events: eventsErrorHandler(config.events ?? {}, logger),\n    adapter: adapterErrorHandler(config.adapter, logger),\n    // Callback functions\n    callbacks: { ...defaultCallbacks, ...config.callbacks },\n    logger,\n    callbackUrl: url.origin,\n    isOnRedirectProxy,\n    experimental: {\n      ...config.experimental,\n    },\n  }\n\n  // Init cookies\n\n  const cookies: cookie.Cookie[] = []\n\n  if (csrfDisabled) {\n    options.csrfTokenVerified = true\n  } else {\n    const {\n      csrfToken,\n      cookie: csrfCookie,\n      csrfTokenVerified,\n    } = await createCSRFToken({\n      options,\n      cookieValue: reqCookies?.[options.cookies.csrfToken.name],\n      isPost,\n      bodyValue: reqCsrfToken,\n    })\n\n    options.csrfToken = csrfToken\n    options.csrfTokenVerified = csrfTokenVerified\n\n    if (csrfCookie) {\n      cookies.push({\n        name: options.cookies.csrfToken.name,\n        value: csrfCookie,\n        options: options.cookies.csrfToken.options,\n      })\n    }\n  }\n\n  const { callbackUrl, callbackUrlCookie } = await createCallbackUrl({\n    options,\n    cookieValue: reqCookies?.[options.cookies.callbackUrl.name],\n    paramValue: reqCallbackUrl,\n  })\n  options.callbackUrl = callbackUrl\n  if (callbackUrlCookie) {\n    cookies.push({\n      name: options.cookies.callbackUrl.name,\n      value: callbackUrlCookie,\n      options: options.cookies.callbackUrl.options,\n    })\n  }\n\n  return { options, cookies }\n}\n\ntype Method = (...args: any[]) => Promise<any>\n\n/** Wraps an object of methods and adds error handling. */\nfunction eventsErrorHandler(\n  methods: Partial<InternalOptions[\"events\"]>,\n  logger: LoggerInstance\n): Partial<InternalOptions[\"events\"]> {\n  return Object.keys(methods).reduce<any>((acc, name) => {\n    acc[name] = async (...args: any[]) => {\n      try {\n        const method: Method = methods[name as keyof Method]\n        return await method(...args)\n      } catch (e) {\n        logger.error(new EventError(e as Error))\n      }\n    }\n    return acc\n  }, {})\n}\n\n/** Handles adapter induced errors. */\nfunction adapterErrorHandler(\n  adapter: AuthConfig[\"adapter\"],\n  logger: LoggerInstance\n) {\n  if (!adapter) return\n\n  return Object.keys(adapter).reduce<any>((acc, name) => {\n    acc[name] = async (...args: any[]) => {\n      try {\n        logger.debug(`adapter_${name}`, { args })\n        const method: Method = adapter[name as keyof Method]\n        return await method(...args)\n      } catch (e) {\n        const error = new AdapterError(e as Error)\n        logger.error(error)\n        throw error\n      }\n    }\n    return acc\n  }, {})\n}\n"
  },
  {
    "path": "packages/core/src/lib/pages/error.tsx",
    "content": "import type { ErrorPageParam, Theme } from \"../../types.js\"\n\n/**\n * The following errors are passed as error query parameters to the default or overridden error page.\n *\n * [Documentation](https://authjs.dev/guides/pages/error)\n */\n\nexport interface ErrorProps {\n  url?: URL\n  theme?: Theme\n  error?: ErrorPageParam\n}\n\ninterface ErrorView {\n  status: number\n  heading: string\n  message: JSX.Element\n  signin?: JSX.Element\n}\n\n/** Renders an error page. */\nexport default function ErrorPage(props: ErrorProps) {\n  const { url, error = \"default\", theme } = props\n  const signinPageUrl = `${url}/signin`\n\n  const errors: Record<ErrorPageParam | \"default\", ErrorView> = {\n    default: {\n      status: 200,\n      heading: \"Error\",\n      message: (\n        <p>\n          <a className=\"site\" href={url?.origin}>\n            {url?.host}\n          </a>\n        </p>\n      ),\n    },\n    Configuration: {\n      status: 500,\n      heading: \"Server error\",\n      message: (\n        <div>\n          <p>There is a problem with the server configuration.</p>\n          <p>Check the server logs for more information.</p>\n        </div>\n      ),\n    },\n    AccessDenied: {\n      status: 403,\n      heading: \"Access Denied\",\n      message: (\n        <div>\n          <p>You do not have permission to sign in.</p>\n          <p>\n            <a className=\"button\" href={signinPageUrl}>\n              Sign in\n            </a>\n          </p>\n        </div>\n      ),\n    },\n    Verification: {\n      status: 403,\n      heading: \"Unable to sign in\",\n      message: (\n        <div>\n          <p>The sign in link is no longer valid.</p>\n          <p>It may have been used already or it may have expired.</p>\n        </div>\n      ),\n      signin: (\n        <a className=\"button\" href={signinPageUrl}>\n          Sign in\n        </a>\n      ),\n    },\n  }\n\n  const { status, heading, message, signin } = errors[error] ?? errors.default\n\n  return {\n    status,\n    html: (\n      <div className=\"error\">\n        {theme?.brandColor && (\n          <style\n            dangerouslySetInnerHTML={{\n              __html: `\n        :root {\n          --brand-color: ${theme?.brandColor}\n        }\n      `,\n            }}\n          />\n        )}\n        <div className=\"card\">\n          {theme?.logo && <img src={theme?.logo} alt=\"Logo\" className=\"logo\" />}\n          <h1>{heading}</h1>\n          <div className=\"message\">{message}</div>\n          {signin}\n        </div>\n      </div>\n    ),\n  }\n}\n"
  },
  {
    "path": "packages/core/src/lib/pages/index.ts",
    "content": "import { renderToString } from \"preact-render-to-string\"\nimport ErrorPage from \"./error.js\"\nimport SigninPage from \"./signin.js\"\nimport SignoutPage from \"./signout.js\"\nimport css from \"./styles.js\"\nimport VerifyRequestPage from \"./verify-request.js\"\nimport { UnknownAction } from \"../../errors.js\"\n\nimport type {\n  InternalOptions,\n  RequestInternal,\n  ResponseInternal,\n  InternalProvider,\n  PublicProvider,\n} from \"../../types.js\"\nimport type { Cookie } from \"../utils/cookie.js\"\n\nfunction send({\n  html,\n  title,\n  status,\n  cookies,\n  theme,\n  headTags,\n}: any): ResponseInternal {\n  return {\n    cookies,\n    status,\n    headers: { \"Content-Type\": \"text/html\" },\n    body: `<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><style>${css}</style><title>${title}</title>${\n      headTags ?? \"\"\n    }</head><body class=\"__next-auth-theme-${\n      theme?.colorScheme ?? \"auto\"\n    }\"><div class=\"page\">${renderToString(html)}</div></body></html>`,\n  }\n}\n\ntype RenderPageParams = {\n  query?: RequestInternal[\"query\"]\n  cookies?: Cookie[]\n} & Partial<\n  Pick<\n    InternalOptions,\n    \"url\" | \"callbackUrl\" | \"csrfToken\" | \"providers\" | \"theme\" | \"pages\"\n  >\n>\n\n/**\n * Unless the user defines their [own pages](https://authjs.dev/reference/core#pages),\n * we render a set of default ones, using Preact SSR.\n */\nexport default function renderPage(params: RenderPageParams) {\n  const { url, theme, query, cookies, pages, providers } = params\n\n  return {\n    csrf(skip: boolean, options: InternalOptions, cookies: Cookie[]) {\n      if (!skip) {\n        return {\n          headers: {\n            \"Content-Type\": \"application/json\",\n            \"Cache-Control\": \"private, no-cache, no-store\",\n            Expires: \"0\",\n            Pragma: \"no-cache\",\n          },\n          body: { csrfToken: options.csrfToken },\n          cookies,\n        }\n      }\n      options.logger.warn(\"csrf-disabled\")\n      cookies.push({\n        name: options.cookies.csrfToken.name,\n        value: \"\",\n        options: { ...options.cookies.csrfToken.options, maxAge: 0 },\n      })\n      return { status: 404, cookies }\n    },\n    providers(providers: InternalProvider[]) {\n      return {\n        headers: { \"Content-Type\": \"application/json\" },\n        body: providers.reduce<Record<string, PublicProvider>>(\n          (acc, { id, name, type, signinUrl, callbackUrl }) => {\n            acc[id] = { id, name, type, signinUrl, callbackUrl }\n            return acc\n          },\n          {}\n        ),\n      }\n    },\n    signin(providerId?: string, error?: any) {\n      if (providerId) throw new UnknownAction(\"Unsupported action\")\n      if (pages?.signIn) {\n        let signinUrl = `${pages.signIn}${\n          pages.signIn.includes(\"?\") ? \"&\" : \"?\"\n        }${new URLSearchParams({ callbackUrl: params.callbackUrl ?? \"/\" })}`\n        if (error) signinUrl = `${signinUrl}&${new URLSearchParams({ error })}`\n        return { redirect: signinUrl, cookies }\n      }\n\n      // If we have a webauthn provider with conditional UI and\n      // a simpleWebAuthnBrowserScript is defined, we need to\n      // render the script in the page.\n      const webauthnProvider = providers?.find(\n        (p): p is InternalProvider<\"webauthn\"> =>\n          p.type === \"webauthn\" &&\n          p.enableConditionalUI &&\n          !!p.simpleWebAuthnBrowserVersion\n      )\n\n      let simpleWebAuthnBrowserScript = \"\"\n      if (webauthnProvider) {\n        const { simpleWebAuthnBrowserVersion } = webauthnProvider\n        simpleWebAuthnBrowserScript = `<script src=\"https://unpkg.com/@simplewebauthn/browser@${simpleWebAuthnBrowserVersion}/dist/bundle/index.umd.min.js\" crossorigin=\"anonymous\"></script>`\n      }\n\n      return send({\n        cookies,\n        theme,\n        html: SigninPage({\n          csrfToken: params.csrfToken,\n          // We only want to render providers\n          providers: params.providers?.filter(\n            (provider) =>\n              // Always render oauth and email type providers\n              [\"email\", \"oauth\", \"oidc\"].includes(provider.type) ||\n              // Only render credentials type provider if credentials are defined\n              (provider.type === \"credentials\" && provider.credentials) ||\n              // Only render webauthn type provider if formFields are defined\n              (provider.type === \"webauthn\" && provider.formFields) ||\n              // Don't render other provider types\n              false\n          ),\n          callbackUrl: params.callbackUrl,\n          theme: params.theme,\n          error,\n          ...query,\n        }),\n        title: \"Sign In\",\n        headTags: simpleWebAuthnBrowserScript,\n      })\n    },\n    signout() {\n      if (pages?.signOut) return { redirect: pages.signOut, cookies }\n      return send({\n        cookies,\n        theme,\n        html: SignoutPage({ csrfToken: params.csrfToken, url, theme }),\n        title: \"Sign Out\",\n      })\n    },\n    verifyRequest(props?: any) {\n      if (pages?.verifyRequest)\n        return {\n          redirect: `${pages.verifyRequest}${url?.search ?? \"\"}`,\n          cookies,\n        }\n      return send({\n        cookies,\n        theme,\n        html: VerifyRequestPage({ url, theme, ...props }),\n        title: \"Verify Request\",\n      })\n    },\n    error(error?: string) {\n      if (pages?.error) {\n        return {\n          redirect: `${pages.error}${\n            pages.error.includes(\"?\") ? \"&\" : \"?\"\n          }error=${error}`,\n          cookies,\n        }\n      }\n      return send({\n        cookies,\n        theme,\n        // @ts-expect-error fix error type\n        ...ErrorPage({ url, theme, error }),\n        title: \"Error\",\n      })\n    },\n  }\n}\n"
  },
  {
    "path": "packages/core/src/lib/pages/signin.tsx",
    "content": "import type {\n  InternalProvider,\n  SignInPageErrorParam,\n  Theme,\n} from \"../../types.js\"\nimport { webauthnScript } from \"../utils/webauthn-client.js\"\n\nconst signinErrors: Record<SignInPageErrorParam | \"default\", string> = {\n  default: \"Unable to sign in.\",\n  Signin: \"Try signing in with a different account.\",\n  OAuthSignin: \"Try signing in with a different account.\",\n  OAuthCallbackError: \"Try signing in with a different account.\",\n  OAuthCreateAccount: \"Try signing in with a different account.\",\n  EmailCreateAccount: \"Try signing in with a different account.\",\n  Callback: \"Try signing in with a different account.\",\n  OAuthAccountNotLinked:\n    \"To confirm your identity, sign in with the same account you used originally.\",\n  EmailSignin: \"The e-mail could not be sent.\",\n  CredentialsSignin:\n    \"Sign in failed. Check the details you provided are correct.\",\n  SessionRequired: \"Please sign in to access this page.\",\n}\n\nfunction ConditionalUIScript(providerID: string) {\n  const startConditionalUIScript = `\nconst currentURL = window.location.href;\nconst authURL = currentURL.substring(0, currentURL.lastIndexOf('/'));\n(${webauthnScript})(authURL, \"${providerID}\");\n`\n  return (\n    <>\n      <script dangerouslySetInnerHTML={{ __html: startConditionalUIScript }} />\n    </>\n  )\n}\n\nexport default function SigninPage(props: {\n  csrfToken?: string\n  providers?: InternalProvider[]\n  callbackUrl?: string\n  email?: string\n  error?: SignInPageErrorParam\n  theme?: Theme\n}) {\n  const {\n    csrfToken,\n    providers = [],\n    callbackUrl,\n    theme,\n    email,\n    error: errorType,\n  } = props\n\n  if (typeof document !== \"undefined\" && theme?.brandColor) {\n    document.documentElement.style.setProperty(\n      \"--brand-color\",\n      theme.brandColor\n    )\n  }\n\n  if (typeof document !== \"undefined\" && theme?.buttonText) {\n    document.documentElement.style.setProperty(\n      \"--button-text-color\",\n      theme.buttonText\n    )\n  }\n\n  const error = errorType && (signinErrors[errorType] ?? signinErrors.default)\n\n  const providerLogoPath = \"https://authjs.dev/img/providers\"\n\n  const conditionalUIProviderID = providers.find(\n    (provider) => provider.type === \"webauthn\" && provider.enableConditionalUI\n  )?.id\n\n  return (\n    <div className=\"signin\">\n      {theme?.brandColor && (\n        <style\n          dangerouslySetInnerHTML={{\n            __html: `:root {--brand-color: ${theme.brandColor}}`,\n          }}\n        />\n      )}\n      {theme?.buttonText && (\n        <style\n          dangerouslySetInnerHTML={{\n            __html: `\n        :root {\n          --button-text-color: ${theme.buttonText}\n        }\n      `,\n          }}\n        />\n      )}\n      <div className=\"card\">\n        {error && (\n          <div className=\"error\">\n            <p>{error}</p>\n          </div>\n        )}\n        {theme?.logo && <img src={theme.logo} alt=\"Logo\" className=\"logo\" />}\n        {providers.map((provider, i) => {\n          let bg, brandColor, logo\n          if (provider.type === \"oauth\" || provider.type === \"oidc\") {\n            ;({\n              bg = \"#fff\",\n              brandColor,\n              logo = `${providerLogoPath}/${provider.id}.svg`,\n            } = provider.style ?? {})\n          }\n          const color = brandColor ?? bg ?? \"#fff\"\n          return (\n            <div key={provider.id} className=\"provider\">\n              {provider.type === \"oauth\" || provider.type === \"oidc\" ? (\n                <form action={provider.signinUrl} method=\"POST\">\n                  <input type=\"hidden\" name=\"csrfToken\" value={csrfToken} />\n                  {callbackUrl && (\n                    <input\n                      type=\"hidden\"\n                      name=\"callbackUrl\"\n                      value={callbackUrl}\n                    />\n                  )}\n                  <button\n                    type=\"submit\"\n                    className=\"button\"\n                    style={{\n                      \"--provider-brand-color\": color,\n                    }}\n                    tabIndex={0}\n                  >\n                    <span\n                      style={{\n                        filter:\n                          \"invert(1) grayscale(1) brightness(1.3) contrast(9000)\",\n                        \"mix-blend-mode\": \"luminosity\",\n                        opacity: 0.95,\n                      }}\n                    >\n                      Sign in with {provider.name}\n                    </span>\n                    {logo && <img loading=\"lazy\" height={24} src={logo} />}\n                  </button>\n                </form>\n              ) : null}\n              {(provider.type === \"email\" ||\n                provider.type === \"credentials\" ||\n                provider.type === \"webauthn\") &&\n                i > 0 &&\n                providers[i - 1].type !== \"email\" &&\n                providers[i - 1].type !== \"credentials\" &&\n                providers[i - 1].type !== \"webauthn\" && <hr />}\n              {provider.type === \"email\" && (\n                <form action={provider.signinUrl} method=\"POST\">\n                  <input type=\"hidden\" name=\"csrfToken\" value={csrfToken} />\n                  <label\n                    className=\"section-header\"\n                    htmlFor={`input-email-for-${provider.id}-provider`}\n                  >\n                    Email\n                  </label>\n                  <input\n                    id={`input-email-for-${provider.id}-provider`}\n                    autoFocus\n                    type=\"email\"\n                    name=\"email\"\n                    value={email}\n                    placeholder=\"email@example.com\"\n                    required\n                  />\n                  <button id=\"submitButton\" type=\"submit\" tabIndex={0}>\n                    Sign in with {provider.name}\n                  </button>\n                </form>\n              )}\n              {provider.type === \"credentials\" && (\n                <form action={provider.callbackUrl} method=\"POST\">\n                  <input type=\"hidden\" name=\"csrfToken\" value={csrfToken} />\n                  {Object.keys(provider.credentials).map((credential) => {\n                    return (\n                      <div key={`input-group-${provider.id}`}>\n                        <label\n                          className=\"section-header\"\n                          htmlFor={`input-${credential}-for-${provider.id}-provider`}\n                        >\n                          {provider.credentials[credential].label ?? credential}\n                        </label>\n                        <input\n                          name={credential}\n                          id={`input-${credential}-for-${provider.id}-provider`}\n                          type={provider.credentials[credential].type ?? \"text\"}\n                          placeholder={\n                            provider.credentials[credential].placeholder ?? \"\"\n                          }\n                          {...provider.credentials[credential]}\n                        />\n                      </div>\n                    )\n                  })}\n                  <button id=\"submitButton\" type=\"submit\" tabIndex={0}>\n                    Sign in with {provider.name}\n                  </button>\n                </form>\n              )}\n              {provider.type === \"webauthn\" && (\n                <form\n                  action={provider.callbackUrl}\n                  method=\"POST\"\n                  id={`${provider.id}-form`}\n                >\n                  <input type=\"hidden\" name=\"csrfToken\" value={csrfToken} />\n                  {Object.keys(provider.formFields).map((field) => {\n                    return (\n                      <div key={`input-group-${provider.id}`}>\n                        <label\n                          className=\"section-header\"\n                          htmlFor={`input-${field}-for-${provider.id}-provider`}\n                        >\n                          {provider.formFields[field].label ?? field}\n                        </label>\n                        <input\n                          name={field}\n                          data-form-field\n                          id={`input-${field}-for-${provider.id}-provider`}\n                          type={provider.formFields[field].type ?? \"text\"}\n                          placeholder={\n                            provider.formFields[field].placeholder ?? \"\"\n                          }\n                          {...provider.formFields[field]}\n                        />\n                      </div>\n                    )\n                  })}\n                  <button\n                    id={`submitButton-${provider.id}`}\n                    type=\"submit\"\n                    tabIndex={0}\n                  >\n                    Sign in with {provider.name}\n                  </button>\n                </form>\n              )}\n              {(provider.type === \"email\" ||\n                provider.type === \"credentials\" ||\n                provider.type === \"webauthn\") &&\n                i + 1 < providers.length && <hr />}\n            </div>\n          )\n        })}\n      </div>\n      {conditionalUIProviderID && ConditionalUIScript(conditionalUIProviderID)}\n    </div>\n  )\n}\n"
  },
  {
    "path": "packages/core/src/lib/pages/signout.tsx",
    "content": "import type { Theme } from \"../../types.js\"\n\nexport interface SignoutProps {\n  url?: URL\n  csrfToken?: string\n  theme?: Theme\n}\n\nexport default function SignoutPage(props: SignoutProps) {\n  const { url, csrfToken, theme } = props\n\n  return (\n    <div className=\"signout\">\n      {theme?.brandColor && (\n        <style\n          dangerouslySetInnerHTML={{\n            __html: `\n        :root {\n          --brand-color: ${theme.brandColor}\n        }\n      `,\n          }}\n        />\n      )}\n      {theme?.buttonText && (\n        <style\n          dangerouslySetInnerHTML={{\n            __html: `\n        :root {\n          --button-text-color: ${theme.buttonText}\n        }\n      `,\n          }}\n        />\n      )}\n      <div className=\"card\">\n        {theme?.logo && <img src={theme.logo} alt=\"Logo\" className=\"logo\" />}\n        <h1>Signout</h1>\n        <p>Are you sure you want to sign out?</p>\n        <form action={url?.toString()} method=\"POST\">\n          <input type=\"hidden\" name=\"csrfToken\" value={csrfToken} />\n          <button id=\"submitButton\" type=\"submit\">\n            Sign out\n          </button>\n        </form>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "packages/core/src/lib/pages/styles.css",
    "content": ":root {\n  --border-width: 1px;\n  --border-radius: 0.5rem;\n  --color-error: #c94b4b;\n  --color-info: #157efb;\n  --color-info-hover: #0f6ddb;\n  --color-info-text: #fff;\n}\n\n.__next-auth-theme-auto,\n.__next-auth-theme-light {\n  --color-background: #ececec;\n  --color-background-hover: rgba(236, 236, 236, 0.8);\n  --color-background-card: #fff;\n  --color-text: #000;\n  --color-primary: #444;\n  --color-control-border: #bbb;\n  --color-button-active-background: #f9f9f9;\n  --color-button-active-border: #aaa;\n  --color-separator: #ccc;\n  --provider-bg: #fff;\n  --provider-bg-hover: color-mix(\n    in srgb,\n    var(--provider-brand-color) 30%,\n    #fff\n  );\n}\n\n.__next-auth-theme-dark {\n  --color-background: #161b22;\n  --color-background-hover: rgba(22, 27, 34, 0.8);\n  --color-background-card: #0d1117;\n  --color-text: #fff;\n  --color-primary: #ccc;\n  --color-control-border: #555;\n  --color-button-active-background: #060606;\n  --color-button-active-border: #666;\n  --color-separator: #444;\n  --provider-bg: #161b22;\n  --provider-bg-hover: color-mix(\n    in srgb,\n    var(--provider-brand-color) 30%,\n    #000\n  );\n  img[src$=\"42-school.svg\"],\n  img[src$=\"apple.svg\"],\n  img[src$=\"boxyhq-saml.svg\"],\n  img[src$=\"eveonline.svg\"],\n  img[src$=\"github.svg\"],\n  img[src$=\"mailchimp.svg\"],\n  img[src$=\"medium.svg\"],\n  img[src$=\"okta.svg\"],\n  img[src$=\"patreon.svg\"],\n  img[src$=\"ping-id.svg\"],\n  img[src$=\"roblox.svg\"],\n  img[src$=\"threads.svg\"],\n  img[src$=\"wikimedia.svg\"] {\n    filter: invert(1);\n  }\n  #submitButton {\n    background-color: var(--provider-bg, var(--color-info));\n  }\n}\n\n@media (prefers-color-scheme: dark) {\n  .__next-auth-theme-auto {\n    --color-background: #161b22;\n    --color-background-hover: rgba(22, 27, 34, 0.8);\n    --color-background-card: #0d1117;\n    --color-text: #fff;\n    --color-primary: #ccc;\n    --color-control-border: #555;\n    --color-button-active-background: #060606;\n    --color-button-active-border: #666;\n    --color-separator: #444;\n    --provider-bg: #161b22;\n    --provider-bg-hover: color-mix(\n      in srgb,\n      var(--provider-brand-color) 30%,\n      #000\n    );\n    img[src$=\"42-school.svg\"],\n    img[src$=\"apple.svg\"],\n    img[src$=\"boxyhq-saml.svg\"],\n    img[src$=\"eveonline.svg\"],\n    img[src$=\"github.svg\"],\n    img[src$=\"mailchimp.svg\"],\n    img[src$=\"medium.svg\"],\n    img[src$=\"okta.svg\"],\n    img[src$=\"patreon.svg\"],\n    img[src$=\"ping-id.svg\"],\n    img[src$=\"roblox.svg\"],\n    img[src$=\"threads.svg\"],\n    img[src$=\"wikimedia.svg\"] {\n      filter: invert(1);\n    }\n    #submitButton {\n      background-color: var(--provider-bg, var(--color-info));\n    }\n  }\n}\n\nhtml {\n  -webkit-box-sizing: border-box;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n}\n\n*,\n*:before,\n*:after {\n  -webkit-box-sizing: inherit;\n  -moz-box-sizing: inherit;\n  box-sizing: inherit;\n  margin: 0;\n  padding: 0;\n}\n\nbody {\n  background-color: var(--color-background);\n  margin: 0;\n  padding: 0;\n  font-family:\n    ui-sans-serif,\n    system-ui,\n    -apple-system,\n    BlinkMacSystemFont,\n    \"Segoe UI\",\n    Roboto,\n    \"Helvetica Neue\",\n    Arial,\n    \"Noto Sans\",\n    sans-serif,\n    \"Apple Color Emoji\",\n    \"Segoe UI Emoji\",\n    \"Segoe UI Symbol\",\n    \"Noto Color Emoji\";\n}\n\nh1 {\n  margin-bottom: 1.5rem;\n  padding: 0 1rem;\n  font-weight: 400;\n  color: var(--color-text);\n}\n\np {\n  margin-bottom: 1.5rem;\n  padding: 0 1rem;\n  color: var(--color-text);\n}\n\nform {\n  margin: 0;\n  padding: 0;\n}\n\nlabel {\n  font-weight: 500;\n  text-align: left;\n  margin-bottom: 0.25rem;\n  display: block;\n  color: var(--color-text);\n}\n\ninput[type] {\n  box-sizing: border-box;\n  display: block;\n  width: 100%;\n  padding: 0.5rem 1rem;\n  border: var(--border-width) solid var(--color-control-border);\n  background: var(--color-background-card);\n  font-size: 1rem;\n  border-radius: var(--border-radius);\n  color: var(--color-text);\n}\n\np {\n  font-size: 1.1rem;\n  line-height: 2rem;\n}\n\na.button {\n  text-decoration: none;\n  line-height: 1rem;\n\n  &:link,\n  &:visited {\n    background-color: var(--color-background);\n    color: var(--color-primary);\n  }\n}\n\nbutton,\na.button {\n  padding: 0.75rem 1rem;\n  color: var(--provider-color, var(--color-primary));\n  background-color: var(--provider-bg, var(--color-background));\n  border: 1px solid #00000031;\n  font-size: 0.9rem;\n  height: 50px;\n  border-radius: var(--border-radius);\n  transition: background-color 250ms ease-in-out;\n  font-weight: 300;\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n\n  &:hover {\n    background-color: var(--provider-bg-hover, var(--color-background-hover));\n    cursor: pointer;\n  }\n\n  &:active {\n    cursor: pointer;\n  }\n\n  span {\n    color: var(--provider-bg);\n  }\n}\n\n#submitButton {\n  color: var(--button-text-color, var(--color-info-text));\n  background-color: var(--brand-color, var(--color-info));\n  width: 100%;\n\n  &:hover {\n    background-color: var(\n      --button-hover-bg,\n      var(--color-info-hover)\n    ) !important;\n  }\n}\n\na.site {\n  color: var(--color-primary);\n  text-decoration: none;\n  font-size: 1rem;\n  line-height: 2rem;\n\n  &:hover {\n    text-decoration: underline;\n  }\n}\n\n.page {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  display: grid;\n  place-items: center;\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n\n  > div {\n    text-align: center;\n  }\n}\n\n.error {\n  a.button {\n    padding-left: 2rem;\n    padding-right: 2rem;\n    margin-top: 0.5rem;\n  }\n\n  .message {\n    margin-bottom: 1.5rem;\n  }\n}\n\n.signin {\n  input[type=\"text\"] {\n    margin-left: auto;\n    margin-right: auto;\n    display: block;\n  }\n\n  hr {\n    display: block;\n    border: 0;\n    border-top: 1px solid var(--color-separator);\n    margin: 2rem auto 1rem auto;\n    overflow: visible;\n\n    &::before {\n      content: \"or\";\n      background: var(--color-background-card);\n      color: #888;\n      padding: 0 0.4rem;\n      position: relative;\n      top: -0.7rem;\n    }\n  }\n\n  .error {\n    background: #f5f5f5;\n    font-weight: 500;\n    border-radius: 0.3rem;\n    background: var(--color-error);\n\n    p {\n      text-align: left;\n      padding: 0.5rem 1rem;\n      font-size: 0.9rem;\n      line-height: 1.2rem;\n      color: var(--color-info-text);\n    }\n  }\n\n  > div,\n  form {\n    display: block;\n\n    input[type] {\n      margin-bottom: 0.5rem;\n    }\n\n    button {\n      width: 100%;\n    }\n  }\n\n  .provider + .provider {\n    margin-top: 1rem;\n  }\n}\n\n.logo {\n  display: inline-block;\n  max-width: 150px;\n  margin: 1.25rem 0;\n  max-height: 70px;\n}\n\n.card {\n  background-color: var(--color-background-card);\n  border-radius: 1rem;\n  padding: 1.25rem 2rem;\n\n  .header {\n    color: var(--color-primary);\n  }\n\n  input[type]::placeholder {\n    color: color-mix(\n      in srgb,\n      var(--color-text) 20%,\n      var(--color-button-active-background)\n    );\n  }\n\n  input[type] {\n    background: color-mix(in srgb, var(--color-background-card) 95%, black);\n  }\n}\n\n.section-header {\n  color: var(--color-text);\n}\n\n@media screen and (min-width: 450px) {\n  .card {\n    margin: 2rem 0;\n    width: 368px;\n  }\n}\n\n@media screen and (max-width: 450px) {\n  .card {\n    margin: 1rem 0;\n    width: 343px;\n  }\n}\n"
  },
  {
    "path": "packages/core/src/lib/pages/verify-request.tsx",
    "content": "import type { Theme } from \"../../types.js\"\n\ninterface VerifyRequestPageProps {\n  url: URL\n  theme: Theme\n}\n\nexport default function VerifyRequestPage(props: VerifyRequestPageProps) {\n  const { url, theme } = props\n\n  return (\n    <div className=\"verify-request\">\n      {theme.brandColor && (\n        <style\n          dangerouslySetInnerHTML={{\n            __html: `\n        :root {\n          --brand-color: ${theme.brandColor}\n        }\n      `,\n          }}\n        />\n      )}\n      <div className=\"card\">\n        {theme.logo && <img src={theme.logo} alt=\"Logo\" className=\"logo\" />}\n        <h1>Check your email</h1>\n        <p>A sign in link has been sent to your email address.</p>\n        <p>\n          <a className=\"site\" href={url.origin}>\n            {url.host}\n          </a>\n        </p>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "packages/core/src/lib/symbols.ts",
    "content": "/**\n * :::danger\n * This option is intended for framework authors.\n * :::\n *\n * Auth.js comes with built-in CSRF protection, but\n * if you are implementing a framework that is already protected against CSRF attacks, you can skip this check by\n * passing this value to {@link AuthConfig.skipCSRFCheck}.\n */\nexport const skipCSRFCheck = Symbol(\"skip-csrf-check\")\n\n/**\n * :::danger\n * This option is intended for framework authors.\n * :::\n *\n * Auth.js returns a web standard {@link Response} by default, but\n * if you are implementing a framework you might want to get access to the raw internal response\n * by passing this value to {@link AuthConfig.raw}.\n */\nexport const raw = Symbol(\"return-type-raw\")\n\n/**\n * :::danger\n * This option allows you to override the default `fetch` function used by the provider\n * to make requests to the provider's OAuth endpoints directly.\n * Used incorrectly, it can have security implications.\n * :::\n *\n * It can be used to support corporate proxies, custom fetch libraries, cache discovery endpoints,\n * add mocks for testing, logging, set custom headers/params for non-spec compliant providers, etc.\n *\n * @example\n * ```ts\n * import { Auth, customFetch } from \"@auth/core\"\n * import GitHub from \"@auth/core/providers/github\"\n *\n * const dispatcher = new ProxyAgent(\"my.proxy.server\")\n * function proxy(...args: Parameters<typeof fetch>): ReturnType<typeof fetch> {\n *   return undici(args[0], { ...(args[1] ?? {}), dispatcher })\n * }\n *\n * const response = await Auth(request, {\n *   providers: [GitHub({ [customFetch]: proxy })]\n * })\n * ```\n *\n * @see https://undici.nodejs.org/#/docs/api/ProxyAgent?id=example-basic-proxy-request-with-local-agent-dispatcher\n * @see https://authjs.dev/guides/corporate-proxy\n */\nexport const customFetch = Symbol(\"custom-fetch\")\n\n/**\n * @internal\n *\n * Used to mark some providers for processing within the core library.\n *\n * **Do not use or you will be fired.**\n */\nexport const conformInternal = Symbol(\"conform-internal\")\n"
  },
  {
    "path": "packages/core/src/lib/utils/actions.ts",
    "content": "import type { AuthAction } from \"../../types.js\"\n\nconst actions: AuthAction[] = [\n  \"providers\",\n  \"session\",\n  \"csrf\",\n  \"signin\",\n  \"signout\",\n  \"callback\",\n  \"verify-request\",\n  \"error\",\n  \"webauthn-options\",\n]\n\nexport function isAuthAction(action: string): action is AuthAction {\n  return actions.includes(action as AuthAction)\n}\n"
  },
  {
    "path": "packages/core/src/lib/utils/assert.ts",
    "content": "import { defaultCookies } from \"./cookie.js\"\nimport {\n  AuthError,\n  DuplicateConditionalUI,\n  ExperimentalFeatureNotEnabled,\n  InvalidCallbackUrl,\n  InvalidEndpoints,\n  MissingAdapter,\n  MissingAdapterMethods,\n  MissingAuthorize,\n  MissingSecret,\n  MissingWebAuthnAutocomplete,\n  UnsupportedStrategy,\n  UntrustedHost,\n} from \"../../errors.js\"\n\nimport type { RequestInternal, SemverString } from \"../../types.js\"\nimport type { WarningCode } from \"../../warnings.js\"\nimport { Adapter } from \"../../adapters.js\"\nimport type { AuthConfig } from \"../../index.js\"\n\ntype ConfigError =\n  | InvalidCallbackUrl\n  | InvalidEndpoints\n  | MissingAdapter\n  | MissingAdapterMethods\n  | MissingAuthorize\n  | MissingSecret\n  | UnsupportedStrategy\n\nlet warned = false\n\nfunction isValidHttpUrl(url: string, baseUrl: string) {\n  try {\n    return /^https?:/.test(\n      new URL(url, url.startsWith(\"/\") ? baseUrl : undefined).protocol\n    )\n  } catch {\n    return false\n  }\n}\n\nfunction isSemverString(version: string): version is SemverString {\n  return /^v\\d+(?:\\.\\d+){0,2}$/.test(version)\n}\n\nlet hasCredentials = false\nlet hasEmail = false\nlet hasWebAuthn = false\n\nconst emailMethods: (keyof Adapter)[] = [\n  \"createVerificationToken\",\n  \"useVerificationToken\",\n  \"getUserByEmail\",\n]\n\nconst sessionMethods: (keyof Adapter)[] = [\n  \"createUser\",\n  \"getUser\",\n  \"getUserByEmail\",\n  \"getUserByAccount\",\n  \"updateUser\",\n  \"linkAccount\",\n  \"createSession\",\n  \"getSessionAndUser\",\n  \"updateSession\",\n  \"deleteSession\",\n]\n\nconst webauthnMethods: (keyof Adapter)[] = [\n  \"createUser\",\n  \"getUser\",\n  \"linkAccount\",\n  \"getAccount\",\n  \"getAuthenticator\",\n  \"createAuthenticator\",\n  \"listAuthenticatorsByUserId\",\n  \"updateAuthenticatorCounter\",\n]\n\n/**\n * Verify that the user configured Auth.js correctly.\n * Good place to mention deprecations as well.\n *\n * This is invoked before the init method, so default values are not available yet.\n */\nexport function assertConfig(\n  request: RequestInternal,\n  options: AuthConfig\n): ConfigError | WarningCode[] {\n  const { url } = request\n  const warnings: WarningCode[] = []\n\n  if (!warned && options.debug) warnings.push(\"debug-enabled\")\n\n  if (!options.trustHost) {\n    return new UntrustedHost(`Host must be trusted. URL was: ${request.url}`)\n  }\n\n  if (!options.secret?.length) {\n    return new MissingSecret(\"Please define a `secret`\")\n  }\n\n  const callbackUrlParam = request.query?.callbackUrl as string | undefined\n\n  if (callbackUrlParam && !isValidHttpUrl(callbackUrlParam, url.origin)) {\n    return new InvalidCallbackUrl(\n      `Invalid callback URL. Received: ${callbackUrlParam}`\n    )\n  }\n\n  const { callbackUrl: defaultCallbackUrl } = defaultCookies(\n    options.useSecureCookies ?? url.protocol === \"https:\"\n  )\n  const callbackUrlCookie =\n    request.cookies?.[\n      options.cookies?.callbackUrl?.name ?? defaultCallbackUrl.name\n    ]\n\n  if (callbackUrlCookie && !isValidHttpUrl(callbackUrlCookie, url.origin)) {\n    return new InvalidCallbackUrl(\n      `Invalid callback URL. Received: ${callbackUrlCookie}`\n    )\n  }\n\n  // Keep track of webauthn providers that use conditional UI\n  let hasConditionalUIProvider = false\n\n  for (const p of options.providers) {\n    const provider = typeof p === \"function\" ? p() : p\n    if (\n      (provider.type === \"oauth\" || provider.type === \"oidc\") &&\n      !(provider.issuer ?? provider.options?.issuer)\n    ) {\n      const { authorization: a, token: t, userinfo: u } = provider\n\n      let key\n      if (typeof a !== \"string\" && !a?.url) key = \"authorization\"\n      else if (typeof t !== \"string\" && !t?.url) key = \"token\"\n      else if (typeof u !== \"string\" && !u?.url) key = \"userinfo\"\n\n      if (key) {\n        return new InvalidEndpoints(\n          `Provider \"${provider.id}\" is missing both \\`issuer\\` and \\`${key}\\` endpoint config. At least one of them is required`\n        )\n      }\n    }\n\n    if (provider.type === \"credentials\") hasCredentials = true\n    else if (provider.type === \"email\") hasEmail = true\n    else if (provider.type === \"webauthn\") {\n      hasWebAuthn = true\n\n      // Validate simpleWebAuthnBrowserVersion\n      if (\n        provider.simpleWebAuthnBrowserVersion &&\n        !isSemverString(provider.simpleWebAuthnBrowserVersion)\n      ) {\n        return new AuthError(\n          `Invalid provider config for \"${provider.id}\": simpleWebAuthnBrowserVersion \"${provider.simpleWebAuthnBrowserVersion}\" must be a valid semver string.`\n        )\n      }\n\n      if (provider.enableConditionalUI) {\n        // Make sure only one webauthn provider has \"enableConditionalUI\" set to true\n        if (hasConditionalUIProvider) {\n          return new DuplicateConditionalUI(\n            `Multiple webauthn providers have 'enableConditionalUI' set to True. Only one provider can have this option enabled at a time`\n          )\n        }\n        hasConditionalUIProvider = true\n\n        // Make sure at least one formField has \"webauthn\" in its autocomplete param\n        const hasWebauthnFormField = Object.values(provider.formFields).some(\n          (f) =>\n            f.autocomplete && f.autocomplete.toString().indexOf(\"webauthn\") > -1\n        )\n        if (!hasWebauthnFormField) {\n          return new MissingWebAuthnAutocomplete(\n            `Provider \"${provider.id}\" has 'enableConditionalUI' set to True, but none of its formFields have 'webauthn' in their autocomplete param`\n          )\n        }\n      }\n    }\n  }\n\n  if (hasCredentials) {\n    const dbStrategy = options.session?.strategy === \"database\"\n    const onlyCredentials = !options.providers.some(\n      (p) => (typeof p === \"function\" ? p() : p).type !== \"credentials\"\n    )\n    if (dbStrategy && onlyCredentials) {\n      return new UnsupportedStrategy(\n        \"Signing in with credentials only supported if JWT strategy is enabled\"\n      )\n    }\n\n    const credentialsNoAuthorize = options.providers.some((p) => {\n      const provider = typeof p === \"function\" ? p() : p\n      return provider.type === \"credentials\" && !provider.authorize\n    })\n    if (credentialsNoAuthorize) {\n      return new MissingAuthorize(\n        \"Must define an authorize() handler to use credentials authentication provider\"\n      )\n    }\n  }\n\n  const { adapter, session } = options\n\n  const requiredMethods: (keyof Adapter)[] = []\n\n  if (\n    hasEmail ||\n    session?.strategy === \"database\" ||\n    (!session?.strategy && adapter)\n  ) {\n    if (hasEmail) {\n      if (!adapter) return new MissingAdapter(\"Email login requires an adapter\")\n      requiredMethods.push(...emailMethods)\n    } else {\n      if (!adapter)\n        return new MissingAdapter(\"Database session requires an adapter\")\n      requiredMethods.push(...sessionMethods)\n    }\n  }\n\n  if (hasWebAuthn) {\n    // Log experimental warning\n    if (options.experimental?.enableWebAuthn) {\n      warnings.push(\"experimental-webauthn\")\n    } else {\n      return new ExperimentalFeatureNotEnabled(\n        \"WebAuthn is an experimental feature. To enable it, set `experimental.enableWebAuthn` to `true` in your config\"\n      )\n    }\n\n    if (!adapter) return new MissingAdapter(\"WebAuthn requires an adapter\")\n    requiredMethods.push(...webauthnMethods)\n  }\n\n  if (adapter) {\n    const missing = requiredMethods.filter((m) => !(m in adapter))\n\n    if (missing.length) {\n      return new MissingAdapterMethods(\n        `Required adapter methods were missing: ${missing.join(\", \")}`\n      )\n    }\n  }\n\n  if (!warned) warned = true\n\n  return warnings\n}\n"
  },
  {
    "path": "packages/core/src/lib/utils/callback-url.ts",
    "content": "import type { InternalOptions } from \"../../types.js\"\n\ninterface CreateCallbackUrlParams {\n  options: InternalOptions\n  /** Try reading value from request body (POST) then from query param (GET) */\n  paramValue?: string\n  cookieValue?: string\n}\n\n/**\n * Get callback URL based on query param / cookie + validation,\n * and add it to `req.options.callbackUrl`.\n */\nexport async function createCallbackUrl({\n  options,\n  paramValue,\n  cookieValue,\n}: CreateCallbackUrlParams) {\n  const { url, callbacks } = options\n\n  let callbackUrl = url.origin\n\n  if (paramValue) {\n    // If callbackUrl form field or query parameter is passed try to use it if allowed\n    callbackUrl = await callbacks.redirect({\n      url: paramValue,\n      baseUrl: url.origin,\n    })\n  } else if (cookieValue) {\n    // If no callbackUrl specified, try using the value from the cookie if allowed\n    callbackUrl = await callbacks.redirect({\n      url: cookieValue,\n      baseUrl: url.origin,\n    })\n  }\n\n  return {\n    callbackUrl,\n    // Save callback URL in a cookie so that it can be used for subsequent requests in signin/signout/callback flow\n    callbackUrlCookie: callbackUrl !== cookieValue ? callbackUrl : undefined,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/lib/utils/cookie.ts",
    "content": "import type {\n  CookieOption,\n  CookiesOptions,\n  LoggerInstance,\n  RequestInternal,\n} from \"../../types.js\"\n\n// Uncomment to recalculate the estimated size\n// of an empty session cookie\n// import * as cookie from \"../vendored/cookie.js\"\n// const { serialize } = cookie\n// console.log(\n//   \"Cookie estimated to be \",\n//   serialize(`__Secure.authjs.session-token.0`, \"\", {\n//     expires: new Date(),\n//     httpOnly: true,\n//     maxAge: Number.MAX_SAFE_INTEGER,\n//     path: \"/\",\n//     sameSite: \"strict\",\n//     secure: true,\n//     domain: \"example.com\",\n//   }).length,\n//   \" bytes\"\n// )\n\nconst ALLOWED_COOKIE_SIZE = 4096\n// Based on commented out section above\nconst ESTIMATED_EMPTY_COOKIE_SIZE = 160\nconst CHUNK_SIZE = ALLOWED_COOKIE_SIZE - ESTIMATED_EMPTY_COOKIE_SIZE\n\n// REVIEW: Is there any way to defer two types of strings?\n\n/** Stringified form of `JWT`. Extract the content with `jwt.decode` */\nexport type JWTString = string\n\nexport type SetCookieOptions = Partial<CookieOption[\"options\"]> & {\n  expires?: Date | string\n  encode?: (val: unknown) => string\n}\n\n/**\n * If `options.session.strategy` is set to `jwt`, this is a stringified `JWT`.\n * In case of `strategy: \"database\"`, this is the `sessionToken` of the session in the database.\n */\nexport type SessionToken<T extends \"jwt\" | \"database\" = \"jwt\"> = T extends \"jwt\"\n  ? JWTString\n  : string\n\n/**\n * Use secure cookies if the site uses HTTPS\n * This being conditional allows cookies to work non-HTTPS development URLs\n * Honour secure cookie option, which sets 'secure' and also adds '__Secure-'\n * prefix, but enable them by default if the site URL is HTTPS; but not for\n * non-HTTPS URLs like http://localhost which are used in development).\n * For more on prefixes see https://googlechrome.github.io/samples/cookie-prefixes/\n *\n * @TODO Review cookie settings (names, options)\n */\nexport function defaultCookies(useSecureCookies: boolean) {\n  const cookiePrefix = useSecureCookies ? \"__Secure-\" : \"\"\n  return {\n    // default cookie options\n    sessionToken: {\n      name: `${cookiePrefix}authjs.session-token`,\n      options: {\n        httpOnly: true,\n        sameSite: \"lax\",\n        path: \"/\",\n        secure: useSecureCookies,\n      },\n    },\n    callbackUrl: {\n      name: `${cookiePrefix}authjs.callback-url`,\n      options: {\n        httpOnly: true,\n        sameSite: \"lax\",\n        path: \"/\",\n        secure: useSecureCookies,\n      },\n    },\n    csrfToken: {\n      // Default to __Host- for CSRF token for additional protection if using useSecureCookies\n      // NB: The `__Host-` prefix is stricter than the `__Secure-` prefix.\n      name: `${useSecureCookies ? \"__Host-\" : \"\"}authjs.csrf-token`,\n      options: {\n        httpOnly: true,\n        sameSite: \"lax\",\n        path: \"/\",\n        secure: useSecureCookies,\n      },\n    },\n    pkceCodeVerifier: {\n      name: `${cookiePrefix}authjs.pkce.code_verifier`,\n      options: {\n        httpOnly: true,\n        sameSite: \"lax\",\n        path: \"/\",\n        secure: useSecureCookies,\n        maxAge: 60 * 15, // 15 minutes in seconds\n      },\n    },\n    state: {\n      name: `${cookiePrefix}authjs.state`,\n      options: {\n        httpOnly: true,\n        sameSite: \"lax\",\n        path: \"/\",\n        secure: useSecureCookies,\n        maxAge: 60 * 15, // 15 minutes in seconds\n      },\n    },\n    nonce: {\n      name: `${cookiePrefix}authjs.nonce`,\n      options: {\n        httpOnly: true,\n        sameSite: \"lax\",\n        path: \"/\",\n        secure: useSecureCookies,\n      },\n    },\n    webauthnChallenge: {\n      name: `${cookiePrefix}authjs.challenge`,\n      options: {\n        httpOnly: true,\n        sameSite: \"lax\",\n        path: \"/\",\n        secure: useSecureCookies,\n        maxAge: 60 * 15, // 15 minutes in seconds\n      },\n    },\n  } as const satisfies CookiesOptions\n}\n\nexport interface Cookie extends CookieOption {\n  value: string\n}\n\ntype Chunks = Record<string, string>\n\nexport class SessionStore {\n  #chunks: Chunks = {}\n  #option: CookieOption\n  #logger: LoggerInstance | Console\n\n  constructor(\n    option: CookieOption,\n    cookies: RequestInternal[\"cookies\"],\n    logger: LoggerInstance | Console\n  ) {\n    this.#logger = logger\n    this.#option = option\n    if (!cookies) return\n\n    const { name: sessionCookiePrefix } = option\n\n    for (const [name, value] of Object.entries(cookies)) {\n      if (!name.startsWith(sessionCookiePrefix) || !value) continue\n      this.#chunks[name] = value\n    }\n  }\n\n  /**\n   * The JWT Session or database Session ID\n   * constructed from the cookie chunks.\n   */\n  get value() {\n    // Sort the chunks by their keys before joining\n    const sortedKeys = Object.keys(this.#chunks).sort((a, b) => {\n      const aSuffix = parseInt(a.split(\".\").pop() || \"0\")\n      const bSuffix = parseInt(b.split(\".\").pop() || \"0\")\n\n      return aSuffix - bSuffix\n    })\n\n    // Use the sorted keys to join the chunks in the correct order\n    return sortedKeys.map((key) => this.#chunks[key]).join(\"\")\n  }\n\n  /** Given a cookie, return a list of cookies, chunked to fit the allowed cookie size. */\n  #chunk(cookie: Cookie): Cookie[] {\n    const chunkCount = Math.ceil(cookie.value.length / CHUNK_SIZE)\n\n    if (chunkCount === 1) {\n      this.#chunks[cookie.name] = cookie.value\n      return [cookie]\n    }\n\n    const cookies: Cookie[] = []\n    for (let i = 0; i < chunkCount; i++) {\n      const name = `${cookie.name}.${i}`\n      const value = cookie.value.substr(i * CHUNK_SIZE, CHUNK_SIZE)\n      cookies.push({ ...cookie, name, value })\n      this.#chunks[name] = value\n    }\n\n    this.#logger.debug(\"CHUNKING_SESSION_COOKIE\", {\n      message: `Session cookie exceeds allowed ${ALLOWED_COOKIE_SIZE} bytes.`,\n      emptyCookieSize: ESTIMATED_EMPTY_COOKIE_SIZE,\n      valueSize: cookie.value.length,\n      chunks: cookies.map((c) => c.value.length + ESTIMATED_EMPTY_COOKIE_SIZE),\n    })\n\n    return cookies\n  }\n\n  /** Returns cleaned cookie chunks. */\n  #clean(): Record<string, Cookie> {\n    const cleanedChunks: Record<string, Cookie> = {}\n    for (const name in this.#chunks) {\n      delete this.#chunks?.[name]\n      cleanedChunks[name] = {\n        name,\n        value: \"\",\n        options: { ...this.#option.options, maxAge: 0 },\n      }\n    }\n    return cleanedChunks\n  }\n\n  /**\n   * Given a cookie value, return new cookies, chunked, to fit the allowed cookie size.\n   * If the cookie has changed from chunked to unchunked or vice versa,\n   * it deletes the old cookies as well.\n   */\n  chunk(value: string, options: Partial<Cookie[\"options\"]>): Cookie[] {\n    // Assume all cookies should be cleaned by default\n    const cookies: Record<string, Cookie> = this.#clean()\n\n    // Calculate new chunks\n    const chunked = this.#chunk({\n      name: this.#option.name,\n      value,\n      options: { ...this.#option.options, ...options },\n    })\n\n    // Update stored chunks / cookies\n    for (const chunk of chunked) {\n      cookies[chunk.name] = chunk\n    }\n\n    return Object.values(cookies)\n  }\n\n  /** Returns a list of cookies that should be cleaned. */\n  clean(): Cookie[] {\n    return Object.values(this.#clean())\n  }\n}\n"
  },
  {
    "path": "packages/core/src/lib/utils/date.ts",
    "content": "/**\n * Takes a number in seconds and returns the date in the future.\n * Optionally takes a second date parameter. In that case\n * the date in the future will be calculated from that date instead of now.\n */\nexport function fromDate(time: number, date = Date.now()) {\n  return new Date(date + time * 1000)\n}\n"
  },
  {
    "path": "packages/core/src/lib/utils/email.ts",
    "content": "import type { Theme } from \"../../types.js\"\n\n/**\n * Email HTML body\n * Insert invisible space into domains from being turned into a hyperlink by email\n * clients like Outlook and Apple mail, as this is confusing because it seems\n * like they are supposed to click on it to sign in.\n *\n * @note We don't add the email address to avoid needing to escape it, if you do, remember to sanitize it!\n */\nexport function html(params: { url: string; host: string; theme: Theme }) {\n  const { url, host, theme } = params\n\n  const escapedHost = host.replace(/\\./g, \"&#8203;.\")\n\n  const brandColor = theme.brandColor || \"#346df1\"\n\n  const buttonText = theme.buttonText || \"#fff\"\n\n  const color = {\n    background: \"#f9f9f9\",\n    text: \"#444\",\n    mainBackground: \"#fff\",\n    buttonBackground: brandColor,\n    buttonBorder: brandColor,\n    buttonText,\n  }\n\n  return `\n<body style=\"background: ${color.background};\">\n  <table width=\"100%\" border=\"0\" cellspacing=\"20\" cellpadding=\"0\"\n    style=\"background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;\">\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        Sign in to <strong>${escapedHost}</strong>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\" style=\"padding: 20px 0;\">\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n          <tr>\n            <td align=\"center\" style=\"border-radius: 5px;\" bgcolor=\"${color.buttonBackground}\"><a href=\"${url}\"\n                target=\"_blank\"\n                style=\"font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;\">Sign\n                in</a></td>\n          </tr>\n        </table>\n      </td>\n    </tr>\n    <tr>\n      <td align=\"center\"\n        style=\"padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};\">\n        If you did not request this email you can safely ignore it.\n      </td>\n    </tr>\n  </table>\n</body>\n`\n}\n\n/** Email Text body (fallback for email clients that don't render HTML, e.g. feature phones) */\nexport function text({ url, host }: { url: string; host: string }) {\n  return `Sign in to ${host}\\n${url}\\n\\n`\n}\n"
  },
  {
    "path": "packages/core/src/lib/utils/env.ts",
    "content": "import type { AuthAction } from \"../../types.js\"\nimport type { AuthConfig } from \"../../index.js\"\nimport { setLogger } from \"./logger.js\"\n\n/**\n *  Set default env variables on the config object\n * @param suppressWarnings intended for framework authors.\n */\nexport function setEnvDefaults(\n  envObject: any,\n  config: AuthConfig,\n  suppressBasePathWarning = false\n) {\n  try {\n    const url = envObject.AUTH_URL\n    if (url) {\n      if (config.basePath) {\n        if (!suppressBasePathWarning) {\n          const logger = setLogger(config)\n          logger.warn(\"env-url-basepath-redundant\")\n        }\n      } else {\n        config.basePath = new URL(url).pathname\n      }\n    }\n  } catch {\n    // Catching and swallowing potential URL parsing errors, we'll fall\n    // back to `/auth` below.\n  } finally {\n    config.basePath ??= `/auth`\n  }\n\n  if (!config.secret?.length) {\n    config.secret = []\n    const secret = envObject.AUTH_SECRET\n    if (secret) config.secret.push(secret)\n    for (const i of [1, 2, 3]) {\n      const secret = envObject[`AUTH_SECRET_${i}`]\n      if (secret) config.secret.unshift(secret)\n    }\n  }\n\n  config.redirectProxyUrl ??= envObject.AUTH_REDIRECT_PROXY_URL\n  config.trustHost ??= !!(\n    envObject.AUTH_URL ??\n    envObject.AUTH_TRUST_HOST ??\n    envObject.VERCEL ??\n    envObject.CF_PAGES ??\n    envObject.NODE_ENV !== \"production\"\n  )\n  config.providers = config.providers.map((provider) => {\n    const { id } = typeof provider === \"function\" ? provider({}) : provider\n    const ID = id.toUpperCase().replace(/-/g, \"_\")\n    const clientId = envObject[`AUTH_${ID}_ID`]\n    const clientSecret = envObject[`AUTH_${ID}_SECRET`]\n    const issuer = envObject[`AUTH_${ID}_ISSUER`]\n    const apiKey = envObject[`AUTH_${ID}_KEY`]\n    const finalProvider =\n      typeof provider === \"function\"\n        ? provider({ clientId, clientSecret, issuer, apiKey })\n        : provider\n    if (finalProvider.type === \"oauth\" || finalProvider.type === \"oidc\") {\n      finalProvider.clientId ??= clientId\n      finalProvider.clientSecret ??= clientSecret\n      finalProvider.issuer ??= issuer\n    } else if (finalProvider.type === \"email\") {\n      finalProvider.apiKey ??= apiKey\n    }\n    return finalProvider\n  })\n}\n\nexport function createActionURL(\n  action: AuthAction,\n  protocol: string,\n  headers: Headers,\n  envObject: any,\n  config: Pick<AuthConfig, \"basePath\" | \"logger\">\n): URL {\n  const basePath = config?.basePath\n  const envUrl = envObject.AUTH_URL ?? envObject.NEXTAUTH_URL\n\n  let url: URL\n  if (envUrl) {\n    url = new URL(envUrl)\n    if (basePath && basePath !== \"/\" && url.pathname !== \"/\") {\n      if (url.pathname !== basePath) {\n        const logger = setLogger(config)\n        logger.warn(\"env-url-basepath-mismatch\")\n      }\n      url.pathname = \"/\"\n    }\n  } else {\n    const detectedHost = headers.get(\"x-forwarded-host\") ?? headers.get(\"host\")\n    const detectedProtocol =\n      headers.get(\"x-forwarded-proto\") ?? protocol ?? \"https\"\n    const _protocol = detectedProtocol.endsWith(\":\")\n      ? detectedProtocol\n      : detectedProtocol + \":\"\n\n    url = new URL(`${_protocol}//${detectedHost}`)\n  }\n\n  // remove trailing slash\n  const sanitizedUrl = url.toString().replace(/\\/$/, \"\")\n\n  if (basePath) {\n    // remove leading and trailing slash\n    const sanitizedBasePath = basePath?.replace(/(^\\/|\\/$)/g, \"\") ?? \"\"\n    return new URL(`${sanitizedUrl}/${sanitizedBasePath}/${action}`)\n  }\n  return new URL(`${sanitizedUrl}/${action}`)\n}\n"
  },
  {
    "path": "packages/core/src/lib/utils/logger.ts",
    "content": "import { AuthError } from \"../../errors.js\"\nimport type { WarningCode } from \"../../warnings.js\"\nimport type { AuthConfig } from \"../../index.js\"\n\n/**\n * Override any of the methods, and the rest will use the default logger.\n *\n * [Documentation](https://authjs.dev/reference/core#authconfig#logger)\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport interface LoggerInstance extends Record<string, Function> {\n  warn: (code: WarningCode) => void\n  error: (error: Error) => void\n  debug: (message: string, metadata?: unknown) => void\n}\n\nconst red = \"\\x1b[31m\"\nconst yellow = \"\\x1b[33m\"\nconst grey = \"\\x1b[90m\"\nconst reset = \"\\x1b[0m\"\n\nconst defaultLogger: LoggerInstance = {\n  error(error) {\n    const name = error instanceof AuthError ? error.type : error.name\n    console.error(`${red}[auth][error]${reset} ${name}: ${error.message}`)\n    if (\n      error.cause &&\n      typeof error.cause === \"object\" &&\n      \"err\" in error.cause &&\n      error.cause.err instanceof Error\n    ) {\n      const { err, ...data } = error.cause\n      console.error(`${red}[auth][cause]${reset}:`, err.stack)\n      if (data)\n        console.error(\n          `${red}[auth][details]${reset}:`,\n          JSON.stringify(data, null, 2)\n        )\n    } else if (error.stack) {\n      console.error(error.stack.replace(/.*/, \"\").substring(1))\n    }\n  },\n  warn(code) {\n    const url = `https://warnings.authjs.dev`\n    console.warn(`${yellow}[auth][warn][${code}]${reset}`, `Read more: ${url}`)\n  },\n  debug(message, metadata) {\n    console.log(\n      `${grey}[auth][debug]:${reset} ${message}`,\n      JSON.stringify(metadata, null, 2)\n    )\n  },\n}\n\n/**\n * Override the built-in logger with user's implementation.\n * Any `undefined` level will use the default logger.\n */\nexport function setLogger(\n  config: Pick<AuthConfig, \"logger\" | \"debug\">\n): LoggerInstance {\n  const newLogger: LoggerInstance = {\n    ...defaultLogger,\n  }\n\n  // Turn off debug logging if `debug` isn't set to `true`\n  if (!config.debug) newLogger.debug = () => {}\n\n  if (config.logger?.error) newLogger.error = config.logger.error\n  if (config.logger?.warn) newLogger.warn = config.logger.warn\n  if (config.logger?.debug) newLogger.debug = config.logger.debug\n\n  config.logger ??= newLogger\n  return newLogger\n}\n"
  },
  {
    "path": "packages/core/src/lib/utils/merge.ts",
    "content": "function isObject(item: unknown): item is object {\n  return item !== null && typeof item === \"object\"\n}\n\n/** Deep merge two or more objects */\nexport function merge<T extends Record<string, unknown>>(\n  target: T,\n  ...sources: Array<Record<string, unknown> | undefined>\n): T & Record<string, unknown> {\n  if (!sources.length) return target\n  const source = sources.shift()\n\n  if (isObject(target) && isObject(source)) {\n    for (const key in source) {\n      if (isObject(source[key])) {\n        if (!isObject(target[key]))\n          (target as Record<string, unknown>)[key] = Array.isArray(source[key])\n            ? []\n            : {}\n        merge(\n          (target as Record<string, unknown>)[key] as T,\n          source[key] as Record<string, unknown>\n        )\n      } else if (source[key] !== undefined)\n        (target as Record<string, unknown>)[key] = source[key]\n    }\n  }\n\n  return merge(target, ...sources)\n}\n"
  },
  {
    "path": "packages/core/src/lib/utils/providers.ts",
    "content": "import { merge } from \"./merge.js\"\n\nimport type {\n  AccountCallback,\n  OAuthConfig,\n  OAuthConfigInternal,\n  OAuthEndpointType,\n  OAuthUserConfig,\n  ProfileCallback,\n} from \"../../providers/index.js\"\nimport type { InternalProvider, Profile } from \"../../types.js\"\nimport { type AuthConfig } from \"../../index.js\"\nimport { customFetch } from \"../symbols.js\"\n\n/**\n * Adds `signinUrl` and `callbackUrl` to each provider\n * and deep merge user-defined options.\n */\nexport default function parseProviders(params: {\n  url: URL\n  providerId?: string\n  config: AuthConfig\n}): {\n  providers: InternalProvider[]\n  provider?: InternalProvider\n} {\n  const { providerId, config } = params\n  const url = new URL(config.basePath ?? \"/auth\", params.url.origin)\n\n  const providers = config.providers.map((p) => {\n    const provider = typeof p === \"function\" ? p() : p\n    const { options: userOptions, ...defaults } = provider\n\n    const id = (userOptions?.id ?? defaults.id) as string\n    // TODO: Support if properties have different types, e.g. authorization: string or object\n    const merged = merge(defaults, userOptions, {\n      signinUrl: `${url}/signin/${id}`,\n      callbackUrl: `${url}/callback/${id}`,\n    })\n\n    if (provider.type === \"oauth\" || provider.type === \"oidc\") {\n      merged.redirectProxyUrl ??=\n        userOptions?.redirectProxyUrl ?? config.redirectProxyUrl\n\n      const normalized = normalizeOAuth(merged) as InternalProvider<\n        \"oauth\" | \"oidc\"\n      >\n      // We currently don't support redirect proxies for response_mode=form_post\n      if (\n        normalized.authorization?.url.searchParams.get(\"response_mode\") ===\n        \"form_post\"\n      ) {\n        delete normalized.redirectProxyUrl\n      }\n\n      // @ts-expect-error Symbols don't get merged by the `merge` function\n      // so we need to do it manually.\n      normalized[customFetch] ??= userOptions?.[customFetch]\n      return normalized\n    }\n\n    return merged as InternalProvider\n  })\n\n  const provider = providers.find(({ id }) => id === providerId)\n  if (providerId && !provider) {\n    const availableProviders = providers.map((p) => p.id).join(\", \")\n    throw new Error(\n      `Provider with id \"${providerId}\" not found. Available providers: [${availableProviders}].`\n    )\n  }\n\n  return { providers, provider }\n}\n\n// TODO: Also add discovery here, if some endpoints/config are missing.\n// We should return both a client and authorization server config.\nfunction normalizeOAuth(\n  c: OAuthConfig<any> | OAuthUserConfig<any>\n): OAuthConfigInternal<any> | object {\n  if (c.issuer) c.wellKnown ??= `${c.issuer}/.well-known/openid-configuration`\n\n  const authorization = normalizeEndpoint(c.authorization, c.issuer)\n  if (authorization && !authorization.url?.searchParams.has(\"scope\")) {\n    authorization.url.searchParams.set(\"scope\", \"openid profile email\")\n  }\n\n  const token = normalizeEndpoint(c.token, c.issuer)\n\n  const userinfo = normalizeEndpoint(c.userinfo, c.issuer)\n\n  const checks = c.checks ?? [\"pkce\"]\n  if (c.redirectProxyUrl) {\n    if (!checks.includes(\"state\")) checks.push(\"state\")\n    c.redirectProxyUrl = `${c.redirectProxyUrl}/callback/${c.id}`\n  }\n\n  return {\n    ...c,\n    authorization,\n    token,\n    checks,\n    userinfo,\n    profile: c.profile ?? defaultProfile,\n    account: c.account ?? defaultAccount,\n  }\n}\n\n/**\n * Returns basic user profile from the userinfo response/`id_token` claims.\n * The returned `id` will become the `account.providerAccountId`. `user.id`\n * and `account.id` are auto-generated UUID's.\n *\n * The result if this function is used to create the `User` in the database.\n * @see https://authjs.dev/reference/core/adapters#user\n * @see https://openid.net/specs/openid-connect-core-1_0.html#IDToken\n * @see https://openid.net/specs/openid-connect-core-1_0.html#\n */\nconst defaultProfile: ProfileCallback<Profile> = (profile) => {\n  return stripUndefined({\n    id: profile.sub ?? profile.id ?? crypto.randomUUID(),\n    name: profile.name ?? profile.nickname ?? profile.preferred_username,\n    email: profile.email,\n    image: profile.picture,\n  })\n}\n\n/**\n * Returns basic OAuth/OIDC values from the token response.\n * @see https://www.ietf.org/rfc/rfc6749.html#section-5.1\n * @see https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse\n * @see https://authjs.dev/reference/core/adapters#account\n */\nconst defaultAccount: AccountCallback = (account) => {\n  return stripUndefined({\n    access_token: account.access_token,\n    id_token: account.id_token,\n    refresh_token: account.refresh_token,\n    expires_at: account.expires_at,\n    scope: account.scope,\n    token_type: account.token_type,\n    session_state: account.session_state,\n  })\n}\n\nfunction stripUndefined<T extends object>(o: T): T {\n  const result = {} as any\n  for (const [k, v] of Object.entries(o)) {\n    if (v !== undefined) result[k] = v\n  }\n  return result as T\n}\n\nfunction normalizeEndpoint(\n  e?: OAuthConfig<any>[OAuthEndpointType],\n  issuer?: string\n): OAuthConfigInternal<any>[OAuthEndpointType] {\n  if (!e && issuer) return\n  if (typeof e === \"string\") {\n    return { url: new URL(e) }\n  }\n  // If e.url is undefined, it's because the provider config\n  // assumes that we will use the issuer endpoint.\n  // The existence of either e.url or provider.issuer is checked in\n  // assert.ts. We fallback to \"https://authjs.dev\" to be able to pass around\n  // a valid URL even if the user only provided params.\n  // NOTE: This need to be checked when constructing the URL\n  // for the authorization, token and userinfo endpoints.\n  const url = new URL(e?.url ?? \"https://authjs.dev\")\n  if (e?.params != null) {\n    for (let [key, value] of Object.entries(e.params)) {\n      if (key === \"claims\") {\n        value = JSON.stringify(value)\n      }\n      url.searchParams.set(key, String(value))\n    }\n  }\n  return {\n    url,\n    request: e?.request,\n    conform: e?.conform,\n    ...(e?.clientPrivateKey ? { clientPrivateKey: e?.clientPrivateKey } : null),\n  }\n}\n\nexport function isOIDCProvider(\n  provider: InternalProvider<\"oidc\" | \"oauth\">\n): provider is InternalProvider<\"oidc\"> {\n  return provider.type === \"oidc\"\n}\n\nexport function isOAuth2Provider(\n  provider: InternalProvider<\"oidc\" | \"oauth\">\n): provider is InternalProvider<\"oauth\"> {\n  return provider.type === \"oauth\"\n}\n\n/** Either OAuth 2 or OIDC */\nexport function isOAuthProvider(\n  provider: InternalProvider<any>\n): provider is InternalProvider<\"oauth\" | \"oidc\"> {\n  return provider.type === \"oauth\" || provider.type === \"oidc\"\n}\n"
  },
  {
    "path": "packages/core/src/lib/utils/session.ts",
    "content": "import type { InternalOptions, User } from \"../../types.js\"\nimport type { SessionStore } from \"./cookie.js\"\n\n/**\n * Returns the currently logged in user, if any.\n */\nexport async function getLoggedInUser(\n  options: InternalOptions,\n  sessionStore: SessionStore\n): Promise<User | null> {\n  const {\n    adapter,\n    jwt,\n    session: { strategy: sessionStrategy },\n  } = options\n\n  const sessionToken = sessionStore.value\n  if (!sessionToken) return null\n\n  // Try to decode JWT\n  if (sessionStrategy === \"jwt\") {\n    const salt = options.cookies.sessionToken.name\n    const payload = await jwt.decode({ ...jwt, token: sessionToken, salt })\n\n    if (payload && payload.sub) {\n      return {\n        id: payload.sub,\n        name: payload.name,\n        email: payload.email,\n        image: payload.picture,\n      }\n    }\n  } else {\n    const userAndSession = await adapter?.getSessionAndUser(sessionToken)\n    if (userAndSession) {\n      return userAndSession.user\n    }\n  }\n\n  return null\n}\n"
  },
  {
    "path": "packages/core/src/lib/utils/web.ts",
    "content": "import * as cookie from \"../vendored/cookie.js\"\nimport { UnknownAction } from \"../../errors.js\"\nimport { setLogger } from \"./logger.js\"\n\nimport type {\n  AuthAction,\n  RequestInternal,\n  ResponseInternal,\n} from \"../../types.js\"\nimport { isAuthAction } from \"./actions.js\"\nimport type { AuthConfig } from \"../../index.js\"\n\nconst { parse: parseCookie, serialize: serializeCookie } = cookie\n\nasync function getBody(req: Request): Promise<Record<string, any> | undefined> {\n  if (!(\"body\" in req) || !req.body || req.method !== \"POST\") return\n\n  const contentType = req.headers.get(\"content-type\")\n  if (contentType?.includes(\"application/json\")) {\n    return await req.json()\n  } else if (contentType?.includes(\"application/x-www-form-urlencoded\")) {\n    const params = new URLSearchParams(await req.text())\n    return Object.fromEntries(params)\n  }\n}\n\nexport async function toInternalRequest(\n  req: Request,\n  config: AuthConfig\n): Promise<RequestInternal | undefined> {\n  try {\n    if (req.method !== \"GET\" && req.method !== \"POST\")\n      throw new UnknownAction(\"Only GET and POST requests are supported\")\n\n    // Defaults are usually set in the `init` function, but this is needed below\n    config.basePath ??= \"/auth\"\n\n    const url = new URL(req.url)\n\n    const { action, providerId } = parseActionAndProviderId(\n      url.pathname,\n      config.basePath\n    )\n\n    return {\n      url,\n      action,\n      providerId,\n      method: req.method,\n      headers: Object.fromEntries(req.headers),\n      body: req.body ? await getBody(req) : undefined,\n      cookies: parseCookie(req.headers.get(\"cookie\") ?? \"\") ?? {},\n      error: url.searchParams.get(\"error\") ?? undefined,\n      query: Object.fromEntries(url.searchParams),\n    }\n  } catch (e) {\n    const logger = setLogger(config)\n    logger.error(e as Error)\n    logger.debug(\"request\", req)\n  }\n}\n\nexport function toRequest(request: RequestInternal): Request {\n  return new Request(request.url, {\n    headers: request.headers,\n    method: request.method,\n    body:\n      request.method === \"POST\"\n        ? JSON.stringify(request.body ?? {})\n        : undefined,\n  })\n}\n\nexport function toResponse(res: ResponseInternal): Response {\n  const headers = new Headers(res.headers)\n\n  res.cookies?.forEach((cookie) => {\n    const { name, value, options } = cookie\n    const cookieHeader = serializeCookie(name, value, options)\n    if (headers.has(\"Set-Cookie\")) headers.append(\"Set-Cookie\", cookieHeader)\n    else headers.set(\"Set-Cookie\", cookieHeader)\n  })\n\n  let body = res.body\n\n  if (headers.get(\"content-type\") === \"application/json\")\n    body = JSON.stringify(res.body)\n  else if (headers.get(\"content-type\") === \"application/x-www-form-urlencoded\")\n    body = new URLSearchParams(res.body).toString()\n\n  const status = res.redirect ? 302 : (res.status ?? 200)\n  const response = new Response(body, { headers, status })\n\n  if (res.redirect) response.headers.set(\"Location\", res.redirect)\n\n  return response\n}\n\n/** Web compatible method to create a hash, using SHA256 */\nexport async function createHash(message: string) {\n  const data = new TextEncoder().encode(message)\n  const hash = await crypto.subtle.digest(\"SHA-256\", data)\n  return Array.from(new Uint8Array(hash))\n    .map((b) => b.toString(16).padStart(2, \"0\"))\n    .join(\"\")\n    .toString()\n}\n\n/** Web compatible method to create a random string of a given length */\nexport function randomString(size: number) {\n  const i2hex = (i: number) => (\"0\" + i.toString(16)).slice(-2)\n  const r = (a: string, i: number): string => a + i2hex(i)\n  const bytes = crypto.getRandomValues(new Uint8Array(size))\n  return Array.from(bytes).reduce(r, \"\")\n}\n\n/** @internal Parse the action and provider id from a URL pathname. */\nexport function parseActionAndProviderId(\n  pathname: string,\n  base: string\n): {\n  action: AuthAction\n  providerId?: string\n} {\n  const a = pathname.match(new RegExp(`^${base}(.+)`))\n\n  if (a === null) throw new UnknownAction(`Cannot parse action at ${pathname}`)\n\n  const actionAndProviderId = a.at(-1)!\n\n  const b = actionAndProviderId.replace(/^\\//, \"\").split(\"/\").filter(Boolean)\n\n  if (b.length !== 1 && b.length !== 2)\n    throw new UnknownAction(`Cannot parse action at ${pathname}`)\n\n  const [action, providerId] = b\n\n  if (!isAuthAction(action))\n    throw new UnknownAction(`Cannot parse action at ${pathname}`)\n\n  if (\n    providerId &&\n    ![\"signin\", \"callback\", \"webauthn-options\"].includes(action)\n  )\n    throw new UnknownAction(`Cannot parse action at ${pathname}`)\n\n  return {\n    action,\n    providerId: providerId == \"undefined\" ? undefined : providerId,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/lib/utils/webauthn-client.js",
    "content": "//@ts-check\n\n// Declare a SimpleWebAuthnBrowser variable as part of \"window\"\n\n/** @typedef {\"authenticate\"} WebAuthnAuthenticate */\n/** @typedef {\"register\"} WebAuthnRegister */\n/** @typedef {WebAuthnRegister | WebAuthnAuthenticate} WebAuthnOptionsAction */\n/**\n * @template {WebAuthnOptionsAction} T\n * @typedef {T extends WebAuthnAuthenticate ?\n *  { options: import(\"@simplewebauthn/types\").PublicKeyCredentialRequestOptionsJSON; action: \"authenticate\" } :\n *  T extends WebAuthnRegister ?\n *  { options: import(\"@simplewebauthn/types\").PublicKeyCredentialCreationOptionsJSON; action: \"register\" } :\n * never\n * } WebAuthnOptionsReturn\n */\n\n/**\n * webauthnScript is the client-side script that handles the webauthn form\n *\n * @param {string} authURL is the URL of the auth API\n * @param {string} providerID is the ID of the webauthn provider\n */\nexport async function webauthnScript(authURL, providerID) {\n  /** @type {typeof import(\"@simplewebauthn/browser\")} */\n  // @ts-ignore\n  const WebAuthnBrowser = window.SimpleWebAuthnBrowser\n\n  /**\n   * Fetch webauthn options from the server\n   *\n   * @template {WebAuthnOptionsAction} T\n   * @param {T | undefined} action action to fetch options for\n   * @returns {Promise<WebAuthnOptionsReturn<T> | undefined>}\n   */\n  async function fetchOptions(action) {\n    // Create the options URL with the action and query parameters\n    const url = new URL(`${authURL}/webauthn-options/${providerID}`)\n\n    if (action) url.searchParams.append(\"action\", action)\n\n    const formFields = getFormFields()\n    formFields.forEach((field) => {\n      url.searchParams.append(field.name, field.value)\n    })\n\n    const res = await fetch(url)\n    if (!res.ok) {\n      console.error(\"Failed to fetch options\", res)\n\n      return\n    }\n\n    return res.json()\n  }\n\n  /**\n   * Get the webauthn form from the page\n   *\n   * @returns {HTMLFormElement}\n   */\n  function getForm() {\n    const formID = `#${providerID}-form`\n    /** @type {HTMLFormElement | null} */\n    const form = document.querySelector(formID)\n    if (!form) throw new Error(`Form '${formID}' not found`)\n\n    return form\n  }\n\n  /**\n   * Get formFields from the form\n   *\n   * @returns {HTMLInputElement[]}\n   */\n  function getFormFields() {\n    const form = getForm()\n    /** @type {HTMLInputElement[]} */\n    const formFields = Array.from(\n      form.querySelectorAll(\"input[data-form-field]\")\n    )\n\n    return formFields\n  }\n\n  /**\n   * Passkey form submission handler.\n   * Takes the input from the form and a few other parameters and submits it to the server.\n   *\n   * @param {WebAuthnOptionsAction} action action to submit\n   * @param {unknown | undefined} data optional data to submit\n   * @returns {Promise<void>}\n   */\n  async function submitForm(action, data) {\n    const form = getForm()\n\n    // If a POST request, create hidden fields in the form\n    // and submit it so the browser redirects on login\n    if (action) {\n      const actionInput = document.createElement(\"input\")\n      actionInput.type = \"hidden\"\n      actionInput.name = \"action\"\n      actionInput.value = action\n      form.appendChild(actionInput)\n    }\n\n    if (data) {\n      const dataInput = document.createElement(\"input\")\n      dataInput.type = \"hidden\"\n      dataInput.name = \"data\"\n      dataInput.value = JSON.stringify(data)\n      form.appendChild(dataInput)\n    }\n\n    return form.submit()\n  }\n\n  /**\n   * Executes the authentication flow by fetching options from the server,\n   * starting the authentication, and submitting the response to the server.\n   *\n   * @param {WebAuthnOptionsReturn<WebAuthnAuthenticate>['options']} options\n   * @param {boolean} autofill Whether or not to use the browser's autofill\n   * @returns {Promise<void>}\n   */\n  async function authenticationFlow(options, autofill) {\n    // Start authentication\n    const authResp = await WebAuthnBrowser.startAuthentication(\n      options,\n      autofill\n    )\n\n    // Submit authentication response to server\n    return await submitForm(\"authenticate\", authResp)\n  }\n\n  /**\n   * @param {WebAuthnOptionsReturn<WebAuthnRegister>['options']} options\n   */\n  async function registrationFlow(options) {\n    // Check if all required formFields are set\n    const formFields = getFormFields()\n    formFields.forEach((field) => {\n      if (field.required && !field.value) {\n        throw new Error(`Missing required field: ${field.name}`)\n      }\n    })\n\n    // Start registration\n    const regResp = await WebAuthnBrowser.startRegistration(options)\n\n    // Submit registration response to server\n    return await submitForm(\"register\", regResp)\n  }\n\n  /**\n   * Attempts to authenticate the user when the page loads\n   * using the browser's autofill popup.\n   *\n   * @returns {Promise<void>}\n   */\n  async function autofillAuthentication() {\n    // if the browser can't handle autofill, don't try\n    if (!WebAuthnBrowser.browserSupportsWebAuthnAutofill()) return\n\n    const res = await fetchOptions(\"authenticate\")\n    if (!res) {\n      console.error(\"Failed to fetch option for autofill authentication\")\n\n      return\n    }\n\n    try {\n      await authenticationFlow(res.options, true)\n    } catch (e) {\n      console.error(e)\n    }\n  }\n\n  /**\n   * Sets up the passkey form by overriding the form submission handler\n   * so that it attempts to authenticate the user when the form is submitted.\n   * If the user is not registered, it will attempt to register them instead.\n   */\n  async function setupForm() {\n    const form = getForm()\n\n    // If the browser can't do WebAuthn, hide the form\n    if (!WebAuthnBrowser.browserSupportsWebAuthn()) {\n      form.style.display = \"none\"\n\n      return\n    }\n\n    if (form) {\n      form.addEventListener(\"submit\", async (e) => {\n        e.preventDefault()\n\n        // Fetch options from the server without assuming that\n        // the user is registered\n        const res = await fetchOptions(undefined)\n        if (!res) {\n          console.error(\"Failed to fetch options for form submission\")\n\n          return\n        }\n\n        // Then execute the appropriate flow\n        if (res.action === \"authenticate\") {\n          try {\n            await authenticationFlow(res.options, false)\n          } catch (e) {\n            console.error(e)\n          }\n        } else if (res.action === \"register\") {\n          try {\n            await registrationFlow(res.options)\n          } catch (e) {\n            console.error(e)\n          }\n        }\n      })\n    }\n  }\n\n  // On page load, setup the form and attempt to authenticate the user.\n  setupForm()\n  autofillAuthentication()\n}\n"
  },
  {
    "path": "packages/core/src/lib/utils/webauthn-utils.ts",
    "content": "import type { WebAuthnProviderType } from \"../../providers/webauthn.js\"\nimport type {\n  Account,\n  Authenticator,\n  Awaited,\n  InternalOptions,\n  RequestInternal,\n  ResponseInternal,\n  User,\n} from \"../../types.js\"\nimport type { Cookie } from \"./cookie.js\"\nimport {\n  AdapterError,\n  AuthError,\n  InvalidProvider,\n  MissingAdapter,\n  WebAuthnVerificationError,\n} from \"../../errors.js\"\nimport { webauthnChallenge } from \"../actions/callback/oauth/checks.js\"\nimport {\n  type AuthenticationResponseJSON,\n  type PublicKeyCredentialCreationOptionsJSON,\n  type PublicKeyCredentialRequestOptionsJSON,\n  type RegistrationResponseJSON,\n} from \"@simplewebauthn/types\"\nimport type {\n  Adapter,\n  AdapterAccount,\n  AdapterAuthenticator,\n} from \"../../adapters.js\"\nimport type { GetUserInfo } from \"../../providers/webauthn.js\"\nimport { randomString } from \"./web.js\"\nimport type {\n  VerifiedAuthenticationResponse,\n  VerifiedRegistrationResponse,\n} from \"@simplewebauthn/server\"\n\nexport type WebAuthnRegister = \"register\"\nexport type WebAuthnAuthenticate = \"authenticate\"\nexport type WebAuthnAction = WebAuthnRegister | WebAuthnAuthenticate\n\ntype InternalOptionsWebAuthn = InternalOptions<WebAuthnProviderType> & {\n  adapter: Required<Adapter>\n}\nexport type WebAuthnOptionsResponseBody =\n  | {\n      action: WebAuthnAuthenticate\n      options: PublicKeyCredentialRequestOptionsJSON\n    }\n  | {\n      action: WebAuthnRegister\n      options: PublicKeyCredentialCreationOptionsJSON\n    }\ntype WebAuthnOptionsResponse = ResponseInternal & {\n  body: WebAuthnOptionsResponseBody\n}\n\nexport type CredentialDeviceType = \"singleDevice\" | \"multiDevice\"\ninterface InternalAuthenticator {\n  providerAccountId: string\n  credentialID: Uint8Array\n  credentialPublicKey: Uint8Array\n  counter: number\n  credentialDeviceType: CredentialDeviceType\n  credentialBackedUp: boolean\n  transports?: AuthenticatorTransport[]\n}\n\ntype RGetUserInfo = Awaited<ReturnType<GetUserInfo>>\n\n/**\n * Infers the WebAuthn options based on the provided parameters.\n *\n * @param action - The WebAuthn action to perform (optional).\n * @param loggedInUser - The logged-in user (optional).\n * @param userInfoResponse - The response containing user information (optional).\n *\n * @returns The WebAuthn action to perform, or null if no inference could be made.\n */\nexport function inferWebAuthnOptions(\n  action: WebAuthnAction | undefined,\n  loggedIn: boolean,\n  userInfoResponse: RGetUserInfo\n): WebAuthnAction | null {\n  const { user, exists = false } = userInfoResponse ?? {}\n\n  switch (action) {\n    case \"authenticate\": {\n      /**\n       * Always allow explicit authentication requests.\n       */\n      return \"authenticate\"\n    }\n    case \"register\": {\n      /**\n       * Registration is only allowed if:\n       * - The user is logged in, meaning the user wants to register a new authenticator.\n       * - The user is not logged in and provided user info that does NOT exist, meaning the user wants to register a new account.\n       */\n      if (user && loggedIn === exists) return \"register\"\n      break\n    }\n    case undefined: {\n      /**\n       * When no explicit action is provided, we try to infer it based on the user info provided. These are the possible cases:\n       * - Logged in users must always send an explit action, so we bail out in this case.\n       * - Otherwise, if no user info is provided, the desired action is authentication without pre-defined authenticators.\n       * - Otherwise, if the user info provided is of an existing user, the desired action is authentication with their pre-defined authenticators.\n       * - Finally, if the user info provided is of a non-existing user, the desired action is registration.\n       */\n      if (!loggedIn) {\n        if (user) {\n          if (exists) {\n            return \"authenticate\"\n          } else {\n            return \"register\"\n          }\n        } else {\n          return \"authenticate\"\n        }\n      }\n      break\n    }\n  }\n\n  // No decision could be made\n  return null\n}\n\n/**\n * Retrieves the registration response for WebAuthn options request.\n *\n * @param options - The internal options for WebAuthn.\n * @param request - The request object.\n * @param user - The user information.\n * @param resCookies - Optional cookies to be included in the response.\n * @returns A promise that resolves to the WebAuthnOptionsResponse.\n */\nexport async function getRegistrationResponse(\n  options: InternalOptionsWebAuthn,\n  request: RequestInternal,\n  user: User & { email: string },\n  resCookies?: Cookie[]\n): Promise<WebAuthnOptionsResponse> {\n  // Get registration options\n  const regOptions = await getRegistrationOptions(options, request, user)\n  // Get signed cookie\n  const { cookie } = await webauthnChallenge.create(\n    options,\n    regOptions.challenge,\n    user\n  )\n\n  return {\n    status: 200,\n    cookies: [...(resCookies ?? []), cookie],\n    body: {\n      action: \"register\" as const,\n      options: regOptions,\n    },\n    headers: {\n      \"Content-Type\": \"application/json\",\n    },\n  }\n}\n\n/**\n * Retrieves the authentication response for WebAuthn options request.\n *\n * @param options - The internal options for WebAuthn.\n * @param request - The request object.\n * @param user - Optional user information.\n * @param resCookies - Optional array of cookies to be included in the response.\n * @returns A promise that resolves to a WebAuthnOptionsResponse object.\n */\nexport async function getAuthenticationResponse(\n  options: InternalOptionsWebAuthn,\n  request: RequestInternal,\n  user?: User,\n  resCookies?: Cookie[]\n): Promise<WebAuthnOptionsResponse> {\n  // Get authentication options\n  const authOptions = await getAuthenticationOptions(options, request, user)\n  // Get signed cookie\n  const { cookie } = await webauthnChallenge.create(\n    options,\n    authOptions.challenge\n  )\n\n  return {\n    status: 200,\n    cookies: [...(resCookies ?? []), cookie],\n    body: {\n      action: \"authenticate\" as const,\n      options: authOptions,\n    },\n    headers: {\n      \"Content-Type\": \"application/json\",\n    },\n  }\n}\n\nexport async function verifyAuthenticate(\n  options: InternalOptionsWebAuthn,\n  request: RequestInternal,\n  resCookies: Cookie[]\n): Promise<{ account: AdapterAccount; user: User }> {\n  const { adapter, provider } = options\n\n  // Get WebAuthn response from request body\n  const data =\n    request.body && typeof request.body.data === \"string\"\n      ? (JSON.parse(request.body.data) as unknown)\n      : undefined\n  if (\n    !data ||\n    typeof data !== \"object\" ||\n    !(\"id\" in data) ||\n    typeof data.id !== \"string\"\n  ) {\n    throw new AuthError(\"Invalid WebAuthn Authentication response\")\n  }\n\n  // Reset the ID so we smooth out implementation differences\n  const credentialID = toBase64(fromBase64(data.id))\n\n  // Get authenticator from database\n  const authenticator = await adapter.getAuthenticator(credentialID)\n  if (!authenticator) {\n    throw new AuthError(\n      `WebAuthn authenticator not found in database: ${JSON.stringify({\n        credentialID,\n      })}`\n    )\n  }\n\n  // Get challenge from request cookies\n  const { challenge: expectedChallenge } = await webauthnChallenge.use(\n    options,\n    request.cookies,\n    resCookies\n  )\n\n  // Verify the response\n  let verification: VerifiedAuthenticationResponse\n  try {\n    const relayingParty = provider.getRelayingParty(options, request)\n    verification = await provider.simpleWebAuthn.verifyAuthenticationResponse({\n      ...provider.verifyAuthenticationOptions,\n      expectedChallenge,\n      response: data as AuthenticationResponseJSON,\n      authenticator: fromAdapterAuthenticator(authenticator),\n      expectedOrigin: relayingParty.origin,\n      expectedRPID: relayingParty.id,\n    })\n  } catch (e: any) {\n    throw new WebAuthnVerificationError(e)\n  }\n\n  const { verified, authenticationInfo } = verification\n\n  // Make sure the response was verified\n  if (!verified) {\n    throw new WebAuthnVerificationError(\n      \"WebAuthn authentication response could not be verified\"\n    )\n  }\n\n  // Update authenticator counter\n  try {\n    const { newCounter } = authenticationInfo\n    await adapter.updateAuthenticatorCounter(\n      authenticator.credentialID,\n      newCounter\n    )\n  } catch (e: any) {\n    throw new AdapterError(\n      `Failed to update authenticator counter. This may cause future authentication attempts to fail. ${JSON.stringify(\n        {\n          credentialID,\n          oldCounter: authenticator.counter,\n          newCounter: authenticationInfo.newCounter,\n        }\n      )}`,\n      e\n    )\n  }\n\n  // Get the account and user\n  const account = await adapter.getAccount(\n    authenticator.providerAccountId,\n    provider.id\n  )\n  if (!account) {\n    throw new AuthError(\n      `WebAuthn account not found in database: ${JSON.stringify({\n        credentialID,\n        providerAccountId: authenticator.providerAccountId,\n      })}`\n    )\n  }\n\n  const user = await adapter.getUser(account.userId)\n  if (!user) {\n    throw new AuthError(\n      `WebAuthn user not found in database: ${JSON.stringify({\n        credentialID,\n        providerAccountId: authenticator.providerAccountId,\n        userID: account.userId,\n      })}`\n    )\n  }\n\n  return {\n    account,\n    user,\n  }\n}\n\nexport async function verifyRegister(\n  options: InternalOptions<WebAuthnProviderType>,\n  request: RequestInternal,\n  resCookies: Cookie[]\n): Promise<{ account: Account; user: User; authenticator: Authenticator }> {\n  const { provider } = options\n\n  // Get WebAuthn response from request body\n  const data =\n    request.body && typeof request.body.data === \"string\"\n      ? (JSON.parse(request.body.data) as unknown)\n      : undefined\n  if (\n    !data ||\n    typeof data !== \"object\" ||\n    !(\"id\" in data) ||\n    typeof data.id !== \"string\"\n  ) {\n    throw new AuthError(\"Invalid WebAuthn Registration response\")\n  }\n\n  // Get challenge from request cookies\n  const { challenge: expectedChallenge, registerData: user } =\n    await webauthnChallenge.use(options, request.cookies, resCookies)\n  if (!user) {\n    throw new AuthError(\n      \"Missing user registration data in WebAuthn challenge cookie\"\n    )\n  }\n\n  // Verify the response\n  let verification: VerifiedRegistrationResponse\n  try {\n    const relayingParty = provider.getRelayingParty(options, request)\n    verification = await provider.simpleWebAuthn.verifyRegistrationResponse({\n      ...provider.verifyRegistrationOptions,\n      expectedChallenge,\n      response: data as RegistrationResponseJSON,\n      expectedOrigin: relayingParty.origin,\n      expectedRPID: relayingParty.id,\n    })\n  } catch (e: any) {\n    throw new WebAuthnVerificationError(e)\n  }\n\n  // Make sure the response was verified\n  if (!verification.verified || !verification.registrationInfo) {\n    throw new WebAuthnVerificationError(\n      \"WebAuthn registration response could not be verified\"\n    )\n  }\n\n  // Build a new account\n  const account = {\n    providerAccountId: toBase64(verification.registrationInfo.credentialID),\n    provider: options.provider.id,\n    type: provider.type,\n  }\n\n  // Build a new authenticator\n  const authenticator = {\n    providerAccountId: account.providerAccountId,\n    counter: verification.registrationInfo.counter,\n    credentialID: toBase64(verification.registrationInfo.credentialID),\n    credentialPublicKey: toBase64(\n      verification.registrationInfo.credentialPublicKey\n    ),\n    credentialBackedUp: verification.registrationInfo.credentialBackedUp,\n    credentialDeviceType: verification.registrationInfo.credentialDeviceType,\n    transports: transportsToString(\n      (data as RegistrationResponseJSON).response\n        .transports as AuthenticatorTransport[]\n    ),\n  }\n\n  // Return created stuff\n  return {\n    user,\n    account,\n    authenticator,\n  }\n}\n\n/**\n * Generates WebAuthn authentication options.\n *\n * @param options - The internal options for WebAuthn.\n * @param request - The request object.\n * @param user - Optional user information.\n * @returns The authentication options.\n */\nasync function getAuthenticationOptions(\n  options: InternalOptionsWebAuthn,\n  request: RequestInternal,\n  user?: User\n) {\n  const { provider, adapter } = options\n\n  // Get the user's authenticators.\n  const authenticators =\n    user && user[\"id\"]\n      ? await adapter.listAuthenticatorsByUserId(user.id)\n      : null\n\n  const relayingParty = provider.getRelayingParty(options, request)\n\n  // Return the authentication options.\n  return await provider.simpleWebAuthn.generateAuthenticationOptions({\n    ...provider.authenticationOptions,\n    rpID: relayingParty.id,\n    allowCredentials: authenticators?.map((a) => ({\n      id: fromBase64(a.credentialID),\n      type: \"public-key\",\n      transports: stringToTransports(a.transports),\n    })),\n  })\n}\n\n/**\n * Generates WebAuthn registration options.\n *\n * @param options - The internal options for WebAuthn.\n * @param request - The request object.\n * @param user - The user information.\n * @returns The registration options.\n */\nasync function getRegistrationOptions(\n  options: InternalOptionsWebAuthn,\n  request: RequestInternal,\n  user: User & { email: string }\n) {\n  const { provider, adapter } = options\n\n  // Get the user's authenticators.\n  const authenticators = user[\"id\"]\n    ? await adapter.listAuthenticatorsByUserId(user.id)\n    : null\n\n  // Generate a random user ID for the credential.\n  // We can do this because we don't use this user ID to link the\n  // credential to the user. Instead, we store actual userID in the\n  // Authenticator object and fetch it via it's credential ID.\n  const userID = randomString(32)\n\n  const relayingParty = provider.getRelayingParty(options, request)\n\n  // Return the registration options.\n  return await provider.simpleWebAuthn.generateRegistrationOptions({\n    ...provider.registrationOptions,\n    userID,\n    userName: user.email,\n    userDisplayName: user.name ?? undefined,\n    rpID: relayingParty.id,\n    rpName: relayingParty.name,\n    excludeCredentials: authenticators?.map((a) => ({\n      id: fromBase64(a.credentialID),\n      type: \"public-key\",\n      transports: stringToTransports(a.transports),\n    })),\n  })\n}\n\nexport function assertInternalOptionsWebAuthn(\n  options: InternalOptions\n): InternalOptionsWebAuthn {\n  const { provider, adapter } = options\n\n  // Adapter is required for WebAuthn\n  if (!adapter)\n    throw new MissingAdapter(\"An adapter is required for the WebAuthn provider\")\n  // Provider must be WebAuthn\n  if (!provider || provider.type !== \"webauthn\") {\n    throw new InvalidProvider(\"Provider must be WebAuthn\")\n  }\n  // Narrow the options type for typed usage later\n  return { ...options, provider, adapter }\n}\n\nfunction fromAdapterAuthenticator(\n  authenticator: AdapterAuthenticator\n): InternalAuthenticator {\n  return {\n    ...authenticator,\n    credentialDeviceType:\n      authenticator.credentialDeviceType as InternalAuthenticator[\"credentialDeviceType\"],\n    transports: stringToTransports(authenticator.transports),\n    credentialID: fromBase64(authenticator.credentialID),\n    credentialPublicKey: fromBase64(authenticator.credentialPublicKey),\n  }\n}\n\nexport function fromBase64(base64: string): Uint8Array {\n  return new Uint8Array(Buffer.from(base64, \"base64\"))\n}\n\nexport function toBase64(bytes: Uint8Array): string {\n  return Buffer.from(bytes).toString(\"base64\")\n}\n\nexport function transportsToString(\n  transports: InternalAuthenticator[\"transports\"]\n) {\n  return transports?.join(\",\")\n}\n\nexport function stringToTransports(\n  tstring: string | undefined | null\n): InternalAuthenticator[\"transports\"] {\n  return tstring\n    ? (tstring.split(\",\") as InternalAuthenticator[\"transports\"])\n    : undefined\n}\n"
  },
  {
    "path": "packages/core/src/lib/vendored/cookie.ts",
    "content": "/**\n * @source https://github.com/jshttp/cookie\n * @author blakeembrey\n * @license MIT\n */\n\n/**\n * This is a workaround to support ESM-only environments, until `cookie` ships ESM builds.\n * @see https://github.com/jshttp/cookie/issues/211\n */\n\n/**\n * RegExp to match cookie-name in RFC 6265 sec 4.1.1\n * This refers out to the obsoleted definition of token in RFC 2616 sec 2.2\n * which has been replaced by the token definition in RFC 7230 appendix B.\n *\n * cookie-name       = token\n * token             = 1*tchar\n * tchar             = \"!\" / \"#\" / \"$\" / \"%\" / \"&\" / \"'\" /\n *                     \"*\" / \"+\" / \"-\" / \".\" / \"^\" / \"_\" /\n *                     \"`\" / \"|\" / \"~\" / DIGIT / ALPHA\n */\nconst cookieNameRegExp = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/\n\n/**\n * RegExp to match cookie-value in RFC 6265 sec 4.1.1\n *\n * cookie-value      = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )\n * cookie-octet      = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E\n *                     ; US-ASCII characters excluding CTLs,\n *                     ; whitespace DQUOTE, comma, semicolon,\n *                     ; and backslash\n */\nconst cookieValueRegExp =\n  /^(\"?)[\\u0021\\u0023-\\u002B\\u002D-\\u003A\\u003C-\\u005B\\u005D-\\u007E]*\\1$/\n\n/**\n * RegExp to match domain-value in RFC 6265 sec 4.1.1\n *\n * domain-value      = <subdomain>\n *                     ; defined in [RFC1034], Section 3.5, as\n *                     ; enhanced by [RFC1123], Section 2.1\n * <subdomain>       = <label> | <subdomain> \".\" <label>\n * <label>           = <let-dig> [ [ <ldh-str> ] <let-dig> ]\n *                     Labels must be 63 characters or less.\n *                     'let-dig' not 'letter' in the first char, per RFC1123\n * <ldh-str>         = <let-dig-hyp> | <let-dig-hyp> <ldh-str>\n * <let-dig-hyp>     = <let-dig> | \"-\"\n * <let-dig>         = <letter> | <digit>\n * <letter>          = any one of the 52 alphabetic characters A through Z in\n *                     upper case and a through z in lower case\n * <digit>           = any one of the ten digits 0 through 9\n *\n * Keep support for leading dot: https://github.com/jshttp/cookie/issues/173\n *\n * > (Note that a leading %x2E (\".\"), if present, is ignored even though that\n * character is not permitted, but a trailing %x2E (\".\"), if present, will\n * cause the user agent to ignore the attribute.)\n */\nconst domainValueRegExp =\n  /^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i\n\n/**\n * RegExp to match path-value in RFC 6265 sec 4.1.1\n *\n * path-value        = <any CHAR except CTLs or \";\">\n * CHAR              = %x01-7F\n *                     ; defined in RFC 5234 appendix B.1\n */\nconst pathValueRegExp = /^[\\u0020-\\u003A\\u003D-\\u007E]*$/\n\nconst __toString = Object.prototype.toString\n\nconst NullObject = /* @__PURE__ */ (() => {\n  const C = function () {}\n  C.prototype = Object.create(null)\n  return C\n})() as unknown as { new (): any }\n\n/**\n * Parse options.\n */\nexport interface ParseOptions {\n  /**\n   * Specifies a function that will be used to decode a [cookie-value](https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.1).\n   * Since the value of a cookie has a limited character set (and must be a simple string), this function can be used to decode\n   * a previously-encoded cookie value into a JavaScript string.\n   *\n   * The default function is the global `decodeURIComponent`, wrapped in a `try..catch`. If an error\n   * is thrown it will return the cookie's original value. If you provide your own encode/decode\n   * scheme you must ensure errors are appropriately handled.\n   *\n   * @default decode\n   */\n  decode?: (str: string) => string | undefined\n}\n\n/**\n * Parse a cookie header.\n *\n * Parse the given cookie header string into an object\n * The object has the various cookies as keys(names) => values\n */\nexport function parse(\n  str: string,\n  options?: ParseOptions\n): Record<string, string | undefined> {\n  const obj: Record<string, string | undefined> = new NullObject()\n  const len = str.length\n  // RFC 6265 sec 4.1.1, RFC 2616 2.2 defines a cookie name consists of one char minimum, plus '='.\n  if (len < 2) return obj\n\n  const dec = options?.decode || decode\n  let index = 0\n\n  do {\n    const eqIdx = str.indexOf(\"=\", index)\n    if (eqIdx === -1) break // No more cookie pairs.\n\n    const colonIdx = str.indexOf(\";\", index)\n    const endIdx = colonIdx === -1 ? len : colonIdx\n\n    if (eqIdx > endIdx) {\n      // backtrack on prior semicolon\n      index = str.lastIndexOf(\";\", eqIdx - 1) + 1\n      continue\n    }\n\n    const keyStartIdx = startIndex(str, index, eqIdx)\n    const keyEndIdx = endIndex(str, eqIdx, keyStartIdx)\n    const key = str.slice(keyStartIdx, keyEndIdx)\n\n    // only assign once\n    if (obj[key] === undefined) {\n      let valStartIdx = startIndex(str, eqIdx + 1, endIdx)\n      let valEndIdx = endIndex(str, endIdx, valStartIdx)\n\n      const value = dec(str.slice(valStartIdx, valEndIdx))\n      obj[key] = value\n    }\n\n    index = endIdx + 1\n  } while (index < len)\n\n  return obj\n}\n\nfunction startIndex(str: string, index: number, max: number) {\n  do {\n    const code = str.charCodeAt(index)\n    if (code !== 0x20 /*   */ && code !== 0x09 /* \\t */) return index\n  } while (++index < max)\n  return max\n}\n\nfunction endIndex(str: string, index: number, min: number) {\n  while (index > min) {\n    const code = str.charCodeAt(--index)\n    if (code !== 0x20 /*   */ && code !== 0x09 /* \\t */) return index + 1\n  }\n  return min\n}\n\n/**\n * Serialize options.\n */\nexport interface SerializeOptions {\n  /**\n   * Specifies a function that will be used to encode a [cookie-value](https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.1).\n   * Since value of a cookie has a limited character set (and must be a simple string), this function can be used to encode\n   * a value into a string suited for a cookie's value, and should mirror `decode` when parsing.\n   *\n   * @default encodeURIComponent\n   */\n  encode?: (str: string) => string\n  /**\n   * Specifies the `number` (in seconds) to be the value for the [`Max-Age` `Set-Cookie` attribute](https://tools.ietf.org/html/rfc6265#section-5.2.2).\n   *\n   * The [cookie storage model specification](https://tools.ietf.org/html/rfc6265#section-5.3) states that if both `expires` and\n   * `maxAge` are set, then `maxAge` takes precedence, but it is possible not all clients by obey this,\n   * so if both are set, they should point to the same date and time.\n   */\n  maxAge?: number\n  /**\n   * Specifies the `Date` object to be the value for the [`Expires` `Set-Cookie` attribute](https://tools.ietf.org/html/rfc6265#section-5.2.1).\n   * When no expiration is set clients consider this a \"non-persistent cookie\" and delete it the current session is over.\n   *\n   * The [cookie storage model specification](https://tools.ietf.org/html/rfc6265#section-5.3) states that if both `expires` and\n   * `maxAge` are set, then `maxAge` takes precedence, but it is possible not all clients by obey this,\n   * so if both are set, they should point to the same date and time.\n   */\n  expires?: Date\n  /**\n   * Specifies the value for the [`Domain` `Set-Cookie` attribute](https://tools.ietf.org/html/rfc6265#section-5.2.3).\n   * When no domain is set clients consider the cookie to apply to the current domain only.\n   */\n  domain?: string\n  /**\n   * Specifies the value for the [`Path` `Set-Cookie` attribute](https://tools.ietf.org/html/rfc6265#section-5.2.4).\n   * When no path is set, the path is considered the [\"default path\"](https://tools.ietf.org/html/rfc6265#section-5.1.4).\n   */\n  path?: string\n  /**\n   * Enables the [`HttpOnly` `Set-Cookie` attribute](https://tools.ietf.org/html/rfc6265#section-5.2.6).\n   * When enabled, clients will not allow client-side JavaScript to see the cookie in `document.cookie`.\n   */\n  httpOnly?: boolean\n  /**\n   * Enables the [`Secure` `Set-Cookie` attribute](https://tools.ietf.org/html/rfc6265#section-5.2.5).\n   * When enabled, clients will only send the cookie back if the browser has a HTTPS connection.\n   */\n  secure?: boolean\n  /**\n   * Enables the [`Partitioned` `Set-Cookie` attribute](https://tools.ietf.org/html/draft-cutler-httpbis-partitioned-cookies/).\n   * When enabled, clients will only send the cookie back when the current domain _and_ top-level domain matches.\n   *\n   * This is an attribute that has not yet been fully standardized, and may change in the future.\n   * This also means clients may ignore this attribute until they understand it. More information\n   * about can be found in [the proposal](https://github.com/privacycg/CHIPS).\n   */\n  partitioned?: boolean\n  /**\n   * Specifies the value for the [`Priority` `Set-Cookie` attribute](https://tools.ietf.org/html/draft-west-cookie-priority-00#section-4.1).\n   *\n   * - `'low'` will set the `Priority` attribute to `Low`.\n   * - `'medium'` will set the `Priority` attribute to `Medium`, the default priority when not set.\n   * - `'high'` will set the `Priority` attribute to `High`.\n   *\n   * More information about priority levels can be found in [the specification](https://tools.ietf.org/html/draft-west-cookie-priority-00#section-4.1).\n   */\n  priority?: \"low\" | \"medium\" | \"high\"\n  /**\n   * Specifies the value for the [`SameSite` `Set-Cookie` attribute](https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-09#section-5.4.7).\n   *\n   * - `true` will set the `SameSite` attribute to `Strict` for strict same site enforcement.\n   * - `'lax'` will set the `SameSite` attribute to `Lax` for lax same site enforcement.\n   * - `'none'` will set the `SameSite` attribute to `None` for an explicit cross-site cookie.\n   * - `'strict'` will set the `SameSite` attribute to `Strict` for strict same site enforcement.\n   *\n   * More information about enforcement levels can be found in [the specification](https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-09#section-5.4.7).\n   */\n  sameSite?: boolean | \"lax\" | \"strict\" | \"none\"\n}\n\n/**\n * Serialize data into a cookie header.\n *\n * Serialize a name value pair into a cookie string suitable for\n * http headers. An optional options object specifies cookie parameters.\n *\n * serialize('foo', 'bar', { httpOnly: true })\n *   => \"foo=bar; httpOnly\"\n */\nexport function serialize(\n  name: string,\n  val: string,\n  options?: SerializeOptions\n): string {\n  const enc = options?.encode || encodeURIComponent\n\n  if (!cookieNameRegExp.test(name)) {\n    throw new TypeError(`argument name is invalid: ${name}`)\n  }\n\n  const value = enc(val)\n\n  if (!cookieValueRegExp.test(value)) {\n    throw new TypeError(`argument val is invalid: ${val}`)\n  }\n\n  let str = name + \"=\" + value\n  if (!options) return str\n\n  if (options.maxAge !== undefined) {\n    if (!Number.isInteger(options.maxAge)) {\n      throw new TypeError(`option maxAge is invalid: ${options.maxAge}`)\n    }\n\n    str += \"; Max-Age=\" + options.maxAge\n  }\n\n  if (options.domain) {\n    if (!domainValueRegExp.test(options.domain)) {\n      throw new TypeError(`option domain is invalid: ${options.domain}`)\n    }\n\n    str += \"; Domain=\" + options.domain\n  }\n\n  if (options.path) {\n    if (!pathValueRegExp.test(options.path)) {\n      throw new TypeError(`option path is invalid: ${options.path}`)\n    }\n\n    str += \"; Path=\" + options.path\n  }\n\n  if (options.expires) {\n    if (\n      !isDate(options.expires) ||\n      !Number.isFinite(options.expires.valueOf())\n    ) {\n      throw new TypeError(`option expires is invalid: ${options.expires}`)\n    }\n\n    str += \"; Expires=\" + options.expires.toUTCString()\n  }\n\n  if (options.httpOnly) {\n    str += \"; HttpOnly\"\n  }\n\n  if (options.secure) {\n    str += \"; Secure\"\n  }\n\n  if (options.partitioned) {\n    str += \"; Partitioned\"\n  }\n\n  if (options.priority) {\n    const priority =\n      typeof options.priority === \"string\"\n        ? options.priority.toLowerCase()\n        : undefined\n    switch (priority) {\n      case \"low\":\n        str += \"; Priority=Low\"\n        break\n      case \"medium\":\n        str += \"; Priority=Medium\"\n        break\n      case \"high\":\n        str += \"; Priority=High\"\n        break\n      default:\n        throw new TypeError(`option priority is invalid: ${options.priority}`)\n    }\n  }\n\n  if (options.sameSite) {\n    const sameSite =\n      typeof options.sameSite === \"string\"\n        ? options.sameSite.toLowerCase()\n        : options.sameSite\n    switch (sameSite) {\n      case true:\n      case \"strict\":\n        str += \"; SameSite=Strict\"\n        break\n      case \"lax\":\n        str += \"; SameSite=Lax\"\n        break\n      case \"none\":\n        str += \"; SameSite=None\"\n        break\n      default:\n        throw new TypeError(`option sameSite is invalid: ${options.sameSite}`)\n    }\n  }\n\n  return str\n}\n\n/**\n * URL-decode string value. Optimized to skip native call when no %.\n */\nfunction decode(str: string): string {\n  if (str.indexOf(\"%\") === -1) return str\n\n  try {\n    return decodeURIComponent(str)\n  } catch (e) {\n    return str\n  }\n}\n\n/**\n * Determine if value is a Date.\n */\nfunction isDate(val: any): val is Date {\n  return __toString.call(val) === \"[object Date]\"\n}\n"
  },
  {
    "path": "packages/core/src/providers/42-school.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#fff\", display: \"flex\", justifyContent: \"space-between\", color: \"#000\", padding: 16}}>\n * <span>Built-in <b>42School</b> integration.</span>\n * <a href=\"https://api.intra.42.fr//\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/42-school.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/42-school\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface UserData {\n  id: number\n  email: string\n  login: string\n  first_name: string\n  last_name: string\n  usual_full_name: null | string\n  usual_first_name: null | string\n  url: string\n  phone: \"hidden\" | string | null\n  displayname: string\n  image_url: string | null\n  \"staff?\": boolean\n  correction_point: number\n  pool_month: string | null\n  pool_year: string | null\n  location: string | null\n  wallet: number\n  anonymize_date: string\n  created_at: string\n  updated_at: string | null\n  alumni: boolean\n  \"is_launched?\": boolean\n}\n\nexport interface CursusUser {\n  grade: string | null\n  level: number\n  skills: Array<{ id: number; name: string; level: number }>\n  blackholed_at: string | null\n  id: number\n  begin_at: string | null\n  end_at: string | null\n  cursus_id: number\n  has_coalition: boolean\n  created_at: string\n  updated_at: string | null\n  user: UserData\n  cursus: { id: number; created_at: string; name: string; slug: string }\n}\n\nexport interface ProjectUser {\n  id: number\n  occurrence: number\n  final_mark: number | null\n  status: \"in_progress\" | \"finished\"\n  \"validated?\": boolean | null\n  current_team_id: number\n  project: {\n    id: number\n    name: string\n    slug: string\n    parent_id: number | null\n  }\n  cursus_ids: number[]\n  marked_at: string | null\n  marked: boolean\n  retriable_at: string | null\n  created_at: string\n  updated_at: string | null\n}\n\nexport interface Achievement {\n  id: number\n  name: string\n  description: string\n  tier: \"none\" | \"easy\" | \"medium\" | \"hard\" | \"challenge\"\n  kind: \"scolarity\" | \"project\" | \"pedagogy\" | \"scolarity\"\n  visible: boolean\n  image: string | null\n  nbr_of_success: number | null\n  users_url: string\n}\n\nexport interface LanguagesUser {\n  id: number\n  language_id: number\n  user_id: number\n  position: number\n  created_at: string\n}\n\nexport interface TitlesUser {\n  id: number\n  user_id: number\n  title_id: number\n  selected: boolean\n  created_at: string\n  updated_at: string | null\n}\n\nexport interface ExpertisesUser {\n  id: number\n  expertise_id: number\n  interested: boolean\n  value: number\n  contact_me: boolean\n  created_at: string\n  user_id: number\n}\n\nexport interface Campus {\n  id: number\n  name: string\n  time_zone: string\n  language: {\n    id: number\n    name: string\n    identifier: string\n    created_at: string\n    updated_at: string | null\n  }\n  users_count: number\n  vogsphere_id: number\n  country: string\n  address: string\n  zip: string\n  city: string\n  website: string\n  facebook: string\n  twitter: string\n  active: boolean\n  email_extension: string\n  default_hidden_phone: boolean\n}\n\nexport interface CampusUser {\n  id: number\n  user_id: number\n  campus_id: number\n  is_primary: boolean\n  created_at: string\n  updated_at: string | null\n}\n\nexport interface Image {\n  link: string\n  versions: {\n    micro: string\n    small: string\n    medium: string\n    large: string\n  }\n}\n\nexport interface FortyTwoProfile extends UserData, Record<string, any> {\n  groups: Array<{ id: string; name: string }>\n  cursus_users: CursusUser[]\n  projects_users: ProjectUser[]\n  languages_users: LanguagesUser[]\n  achievements: Achievement[]\n  titles: Array<{ id: string; name: string }>\n  titles_users: TitlesUser[]\n  partnerships: any[]\n  patroned: any[]\n  patroning: any[]\n  expertises_users: ExpertisesUser[]\n  roles: Array<{ id: string; name: string }>\n  campus: Campus[]\n  campus_users: CampusUser[]\n  image: Image\n  user: any | null\n}\n\n/**\n * Add 42School login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/42-school\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import FortyTwoSchool from \"@auth/core/providers/42-school\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     FortyTwoSchool({\n *       clientId: FORTY_TWO_SCHOOL_CLIENT_ID,\n *       clientSecret: FORTY_TWO_SCHOOL_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [42School OAuth documentation](https://api.intra.42.fr/apidoc/guides/web_application_flow)\n *\n * ### Notes\n *\n *\n * :::note\n * 42 returns a field on `Account` called `created_at` which is a number. See the [docs](https://api.intra.42.fr/apidoc/guides/getting_started#make-basic-requests). Make sure to add this field to your database schema, in case if you are using an [Adapter](https://authjs.dev/reference/core/adapters).\n * :::\n * By default, Auth.js assumes that the 42School provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The 42School provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/42-school.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function FortyTwo<P extends FortyTwoProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"42-school\",\n    name: \"42 School\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://api.intra.42.fr/oauth/authorize\",\n      params: { scope: \"public\" },\n    },\n    token: \"https://api.intra.42.fr/oauth/token\",\n    userinfo: \"https://api.intra.42.fr/v2/me\",\n    profile(profile) {\n      return {\n        id: profile.id.toString(),\n        name: profile.usual_full_name,\n        email: profile.email,\n        image: profile.image.link,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/apple.ts",
    "content": "/**\n * <div class=\"provider\" style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\"}}>\n * <span style={{fontSize: \"1.35rem\" }}>\n *  Built-in sign in with <b>Apple</b> integration.\n * </span>\n * <a href=\"https://apple.com\" style={{backgroundColor: \"black\", padding: \"12px\", borderRadius: \"100%\" }}>\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/apple.svg\" width=\"24\"/>\n * </a>\n * </div>\n *\n * @module providers/apple\n */\n\nimport { conformInternal, customFetch } from \"../lib/symbols.js\"\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/** The returned user profile from Apple when using the profile callback. */\nexport interface AppleProfile extends Record<string, any> {\n  /**\n   * The issuer registered claim identifies the principal that issued the identity token.\n   * Since Apple generates the token, the value is `https://appleid.apple.com`.\n   */\n  iss: \"https://appleid.apple.com\"\n  /**\n   * The audience registered claim identifies the recipient for which the identity token is intended.\n   * Since the token is meant for your application, the value is the `client_id` from your developer account.\n   */\n  aud: string\n  /**\n   * The issued at registered claim indicates the time at which Apple issued the identity token,\n   * in terms of the number of seconds since Epoch, in UTC.\n   */\n  iat: number\n\n  /**\n   * The expiration time registered identifies the time on or after which the identity token expires,\n   * in terms of number of seconds since Epoch, in UTC.\n   * The value must be greater than the current date/time when verifying the token.\n   */\n  exp: number\n  /**\n   * The subject registered claim identifies the principal that's the subject of the identity token.\n   * Since this token is meant for your application, the value is the unique identifier for the user.\n   */\n  sub: string\n  /**\n   * A String value used to associate a client session and the identity token.\n   * This value mitigates replay attacks and is present only if passed during the authorization request.\n   */\n  nonce: string\n\n  /**\n   * A Boolean value that indicates whether the transaction is on a nonce-supported platform.\n   * If you sent a nonce in the authorization request but don't see the nonce claim in the identity token,\n   * check this claim to determine how to proceed.\n   * If this claim returns true, you should treat nonce as mandatory and fail the transaction;\n   * otherwise, you can proceed treating the nonce as options.\n   */\n  nonce_supported: boolean\n\n  /**\n   * A String value representing the user's email address.\n   * The email address is either the user's real email address or the proxy address,\n   * depending on their status private email relay service.\n   */\n  email: string\n\n  /**\n   * A String or Boolean value that indicates whether the service has verified the email.\n   * The value of this claim is always true, because the servers only return verified email addresses.\n   * The value can either be a String (`\"true\"`) or a Boolean (`true`).\n   */\n  email_verified: \"true\" | true\n\n  /**\n   * A String or Boolean value that indicates whether the email shared by the user is the proxy address.\n   * The value can either be a String (`\"true\"` or `\"false\"`) or a Boolean (`true` or `false`).\n   */\n  is_private_email: boolean | \"true\" | \"false\"\n\n  /**\n   * An Integer value that indicates whether the user appears to be a real person.\n   * Use the value of this claim to mitigate fraud. The possible values are: 0 (or Unsupported), 1 (or Unknown), 2 (or LikelyReal).\n   * For more information, see [`ASUserDetectionStatus`](https://developer.apple.com/documentation/authenticationservices/asuserdetectionstatus).\n   * This claim is present only on iOS 14 and later, macOS 11 and later, watchOS 7 and later, tvOS 14 and later;\n   * the claim isn't present or supported for web-based apps.\n   */\n  real_user_status: 0 | 1 | 2\n\n  /**\n   * A String value representing the transfer identifier used to migrate users to your team.\n   * This claim is present only during the 60-day transfer period after an you transfer an app.\n   * For more information, see [Bringing New Apps and Users into Your Team](https://developer.apple.com/documentation/sign_in_with_apple/bringing_new_apps_and_users_into_your_team).\n   */\n  transfer_sub: string\n  at_hash: string\n  auth_time: number\n  user?: AppleNonConformUser\n}\n\n/**\n * This is the shape of the `user` query parameter that Apple sends the first\n * time the user consents to the app.\n * @see https://developer.apple.com/documentation/sign_in_with_apple/request_an_authorization_to_the_sign_in_with_apple_server#4066168\n */\nexport interface AppleNonConformUser {\n  name: {\n    firstName: string\n    lastName: string\n  }\n  email: string\n}\n\n/**\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/auth/callback/apple\n * ```\n *\n * #### Configuration\n * ```ts\n * import Apple from \"@auth/core/providers/apple\"\n * ...\n * providers: [\n *   Apple({\n *     clientId: env.AUTH_APPLE_ID,\n *     clientSecret: env.AUTH_APPLE_SECRET,\n *   })\n * ]\n * ...\n * ```\n *\n * ### Resources\n *\n * - Sign in with Apple [Overview](https://developer.apple.com/sign-in-with-apple/get-started/)\n * - Sign in with Apple [REST API](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api)\n * - [How to retrieve](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple#3383773) the user's information from Apple ID servers\n * - [Learn more about OAuth](https://authjs.dev/concepts/oauth)\n * - [Creating the Client Secret](https://developer.apple.com/documentation/accountorganizationaldatasharing/creating-a-client-secret)\n *\n * ### Notes\n *\n * - Apple does not support localhost/http URLs. You can only use a live URL with HTTPS.\n * - Apple requires the client secret to be a JWT. We provide a CLI command `npx auth add apple`, to help you generate one.\n *   This will prompt you for the necessary information and at the end it will add the `AUTH_APPLE_ID` and `AUTH_APPLE_SECRET` to your `.env` file.\n * - Apple provides minimal user information. It returns the user's email and name, but only the first time the user consents to the app.\n * - The Apple provider does not support setting up the same client for multiple deployments (like [preview deployments](https://authjs.dev/getting-started/deployment#securing-a-preview-deployment)).\n * - The Apple provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/apple.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * ## Help\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n */\nexport default function Apple(\n  config: OAuthUserConfig<AppleProfile>\n): OAuthConfig<AppleProfile> {\n  return {\n    id: \"apple\",\n    name: \"Apple\",\n    type: \"oidc\",\n    issuer: \"https://appleid.apple.com\",\n    authorization: {\n      params: {\n        scope: \"name email\", // https://developer.apple.com/documentation/sign_in_with_apple/clientconfigi/3230955-scope\n        response_mode: \"form_post\",\n      },\n    },\n    // We need to parse the special `user` parameter the first time the user consents to the app.\n    // It adds the `name` object to the `profile`, with `firstName` and `lastName` fields.\n    [conformInternal]: true,\n    profile(profile) {\n      const name = profile.user\n        ? `${profile.user.name.firstName} ${profile.user.name.lastName}`\n        : profile.email\n      return {\n        id: profile.sub,\n        name: name,\n        email: profile.email,\n        image: null,\n      }\n    },\n    // Apple does not provide a userinfo endpoint.\n    async [customFetch](...args) {\n      const url = new URL(args[0] instanceof Request ? args[0].url : args[0])\n      if (url.pathname.endsWith(\".well-known/openid-configuration\")) {\n        const response = await fetch(...args)\n        const json = await response.clone().json()\n        return Response.json({\n          ...json,\n          userinfo_endpoint: \"https://appleid.apple.com/fake_endpoint\",\n        })\n      }\n      return fetch(...args)\n    },\n    client: { token_endpoint_auth_method: \"client_secret_post\" },\n    style: { text: \"#fff\", bg: \"#000\" },\n    checks: [\"nonce\", \"state\"],\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/asgardeo.ts",
    "content": "/**\n * <div class=\"provider\" style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\"}}>\n * <span style={{fontSize: \"1.35rem\" }}>\n *  Built-in sign in with <b>Asgardeo</b> integration.\n * </span>\n * <a href=\"https://wso2.com/asgardeo/\" style={{backgroundColor: \"#ECEFF1\", padding: \"12px\", borderRadius: \"100%\" }}>\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/asgardeo.svg\" width=\"24\"/>\n * </a>\n * </div>\n *\n * @module providers/asgardeo\n */\n\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\n/** The returned user profile from Asgardeo when using the profile callback. */\nexport interface AsgardeoProfile extends Record<string, any> {\n  /**\n   * The user Asgardeo account ID\n   */\n  sub: string\n  /**\n   * The user name\n   */\n  given_name: string\n  /**\n   * The user email\n   */\n  email: string\n  /**\n   * The user profile picture\n   */\n  picture: string\n}\n\n/**\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/asgardeo\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Asgardeo from \"@auth/core/providers/asgardeo\";\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Asgardeo({\n *       clientId: ASGARDEO_CLIENT_ID,\n *       clientSecret: ASGARDEO_CLIENT_SECRET,\n *       issuer: ASGARDEO_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Configuring Asgardeo\n *\n * Follow these steps:\n *\n * 1. Log into the [Asgardeo console](https://console.asgardeo.io)\n * 2. Next, go to \"Application\" tab (more info [here](https://wso2.com/asgardeo/docs/guides/applications/register-oidc-web-app/))\n * 3. Register a standard based, Open ID connect, application\n * 4. Add the **callback URLs**: `http://localhost:3000/api/auth/callback/asgardeo` (development) and `https://{YOUR_DOMAIN}.com/api/auth/callback/asgardeo` (production)\n * 5. After registering the application, go to \"Protocol\" tab.\n * 6. Check `code` as the grant type.\n * 7. Add \"Authorized redirect URLs\" & \"Allowed origins fields\"\n * 8. Make Email, First Name, Photo URL user attributes mandatory from the console.\n *\n * Then, create a `.env` file in the project root add the following entries:\n *\n * ```\n * ASGARDEO_CLIENT_ID=\"Copy client ID from protocol tab here\"\n * ASGARDEO_CLIENT_SECRET=\"Copy client from protocol tab here\"\n * ASGARDEO_ISSUER=\"Copy the issuer url from the info tab here\"\n * ```\n *\n * ### Resources\n *\n * - [Asgardeo - Authentication Guide](https://wso2.com/asgardeo/docs/guides/authentication)\n * - [Learn more about OAuth](https://authjs.dev/concepts/oauth)\n *\n * ### Notes\n *\n * The Asgardeo provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/asgardeo.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::info\n * By default, Auth.js assumes that the Asgardeo provider is based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) spec\n * :::\n *\n * ## Help\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n */\nexport default function Asgardeo(\n  config: OIDCUserConfig<AsgardeoProfile>\n): OIDCConfig<AsgardeoProfile> {\n  return {\n    id: \"asgardeo\",\n    name: \"Asgardeo\",\n    type: \"oidc\",\n    wellKnown: `${config?.issuer}/oauth2/token/.well-known/openid-configuration`,\n    style: {\n      bg: \"#000\",\n      text: \"#fff\",\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/atlassian.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\"}}>\n * <span style={{fontSize: \"1.35rem\" }}>\n *  Built-in sign in with <b>Atlassian</b> integration.\n * </span>\n * <a href=\"https://www.atlassian.com/\" style={{backgroundColor: \"black\", padding: \"12px\", borderRadius: \"100%\" }}>\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/atlassian.svg\" width=\"24\" style={{ marginTop: \"-3px\"}} />\n * </a>\n * </div>\n *\n * @module providers/atlassian\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/** The returned user profile from Atlassian when using the profile callback. */\nexport interface AtlassianProfile extends Record<string, any> {\n  /**\n   * The user's atlassian account ID\n   */\n  account_id: string\n  /**\n   * The user name\n   */\n  name: string\n  /**\n   * The user's email\n   */\n  email: string\n  /**\n   * The user's profile picture\n   */\n  picture: string\n}\n\n/**\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/atlassian\n * ```\n *\n * #### Configuration\n *\n * Import the provider and configure it in your **Auth.js** initialization file:\n *\n * ```ts\n * import Atlassian from \"@auth/core/providers/atlassian\"\n * ...\n * providers: [\n *  Atlassian({\n *    clientId: env.AUTH_ATLASSIAN_ID,\n *    clientSecret: env.AUTH_ATLASSIAN_SECRET,\n *  }),\n * ]\n * ...\n * ```\n *\n * ### Configuring Atlassian\n *\n * Follow these steps:\n *\n * 1. From any page on [developer.atlassian.com](https://developer.atlassian.com), select your profile icon in the top-right corner, and from the dropdown, select **Developer console**.\n * 2. Select your app from the list (or create one if you don't already have one)\n * 3. Select **Authorization** in the left menu\n * 4. Next to OAuth 2.0 (3LO), select **Configure** (or **Add** for newly created app)\n * 5. Enter the **Callback URL**: `https://{YOUR_DOMAIN}/api/auth/callback/atlassian`\n * 6. Click Save changes\n * 7. Select **Settings** in the left menu\n * 8. Access and copy your app's **Client ID** and **Secret**\n *\n * Then, create a `.env` file in the project root add the following entries:\n *\n * ```\n * AUTH_ATLASSIAN_ID=<Client ID copied in step 8>\n * AUTH_ATLASSIAN_SECRET=<Secret copied in step 8>\n * ```\n *\n * ### Resources\n *\n * - [Atlassian docs](https://developer.atlassian.com/cloud/jira/software/oauth-2-3lo-apps/)\n *\n * ### Notes\n *\n * The Atlassian provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/atlassian.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).\n *\n * ## Help\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n */\nexport default function Atlassian(\n  options: OAuthUserConfig<AtlassianProfile>\n): OAuthConfig<AtlassianProfile> {\n  return {\n    id: \"atlassian\",\n    name: \"Atlassian\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://auth.atlassian.com/authorize\",\n      params: { audience: \"api.atlassian.com\", scope: \"read:me\" },\n    },\n    token: \"https://auth.atlassian.com/oauth/token\",\n    userinfo: \"https://api.atlassian.com/me\",\n    profile(profile) {\n      return {\n        id: profile.account_id,\n        name: profile.name,\n        email: profile.email,\n        image: profile.picture,\n      }\n    },\n    checks: [\"state\"],\n    style: { bg: \"#fff\", text: \"#0052cc\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/auth0.ts",
    "content": "/**\n * <div class=\"provider\" style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\"}}>\n * <span style={{fontSize: \"1.35rem\" }}>\n *  Built-in sign in with <b>Auth0</b> integration.\n * </span>\n * <a href=\"https://auth0.com\" style={{backgroundColor: \"black\", padding: \"12px\", borderRadius: \"100%\" }}>\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/auth0.svg\" width=\"24\"/>\n * </a>\n * </div>\n *\n * @module providers/auth0\n */\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\n/** The returned user profile from Auth0 when using the profile callback. [Reference](https://auth0.com/docs/manage-users/user-accounts/user-profiles/user-profile-structure). */\nexport interface Auth0Profile extends Record<string, any> {\n  /** The user's unique identifier. */\n  sub: string\n  /** Custom fields that store info about a user that influences the user's access, such as support plan, security roles (if not using the Authorization Core feature set), or access control groups. To learn more, read Metadata Overview. */\n  app_metadata: object\n  /** Indicates whether the user has been blocked. Importing enables subscribers to ensure that users remain blocked when migrating to Auth0. */\n  blocked: boolean\n  /** Timestamp indicating when the user profile was first created. */\n  created_at: Date\n  /** (unique) The user's email address. */\n  email: string\n  /** Indicates whether the user has verified their email address. */\n  email_verified: boolean\n  /** The user's family name. */\n  family_name: string\n  /** The user's given name. */\n  given_name: string\n  /** Custom fields that store info about a user that does not impact what they can or cannot access, such as work address, home address, or user preferences. To learn more, read Metadata Overview. */\n  user_metadata: object\n  /** (unique) The user's username. */\n  username: string\n  /** Contains info retrieved from the identity provider with which the user originally authenticates. Users may also link their profile to multiple identity providers; those identities will then also appear in this array. The contents of an individual identity provider object varies by provider. In some cases, it will also include an API Access Token to be used with the provider. */\n  identities: Array<{\n    /** Name of the Auth0 connection used to authenticate the user. */\n    connection: string\n    /** Indicates whether the connection is a social one. */\n    isSocial: boolean\n    /** Name of the entity that is authenticating the user, such as Facebook, Google, SAML, or your own provider. */\n    provider: string\n    /** User's unique identifier for this connection/provider. */\n    user_id: string\n    /** User info associated with the connection. When profiles are linked, it is populated with the associated user info for secondary accounts. */\n    profileData: object\n    [key: string]: any\n  }>\n  /** IP address associated with the user's last login. */\n  last_ip: string\n  /** Timestamp indicating when the user last logged in. If a user is blocked and logs in, the blocked session updates last_login. If you are using this property from inside a Rule using the user< object, its value will be associated with the login that triggered the rule; this is because rules execute after login. */\n  last_login: Date\n  /** Timestamp indicating the last time the user's password was reset/changed. At user creation, this field does not exist. This property is only available for Database connections. */\n  last_password_reset: Date\n  /** Number of times the user has logged in. If a user is blocked and logs in, the blocked session is counted in logins_count. */\n  logins_count: number\n  /** List of multi-factor providers with which the user is enrolled. */\n  multifactor: string\n  /** The user's full name. */\n  name: string\n  /** The user's nickname. */\n  nickname: string\n  /** The user's phone number. Only valid for users with SMS connections. */\n  phone_number: string\n  /** Indicates whether the user has been verified their phone number. Only valid for users with SMS connections. */\n  phone_verified: boolean\n  /** URL pointing to the user's profile picture. */\n  picture: string\n  /** Timestamp indicating when the user's profile was last updated/modified. Changes to last_login are considered updates, so most of the time, updated_at will match last_login. */\n  updated_at: Date\n  /** (unique) The user's identifier. Importing allows user records to be synchronized across multiple systems without using mapping tables. */\n  user_id: string\n}\n\n/**\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/auth0\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Auth0 from \"@auth/core/providers/auth0\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Auth0({\n *       clientId: AUTH0_ID,\n *       clientSecret: AUTH0_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Auth0 docs](https://auth0.com/docs/authenticate)\n *\n * ### Notes\n *\n * The Auth0 provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/auth0.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * ## Help\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n */\nexport default function Auth0(\n  config: OIDCUserConfig<Auth0Profile>\n): OIDCConfig<Auth0Profile> {\n  return {\n    id: \"auth0\",\n    name: \"Auth0\",\n    type: \"oidc\",\n    style: { text: \"#fff\", bg: \"#EB5424\" },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/authentik.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#fd4b2d\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Authentik</b> integration.</span>\n * <a href=\"https://goauthentik.io/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/authentik.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/authentik\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface AuthentikProfile extends Record<string, any> {\n  iss: string\n  sub: string\n  aud: string\n  exp: number\n  iat: number\n  auth_time: number\n  acr: string\n  c_hash: string\n  nonce: string\n  at_hash: string\n  email: string\n  email_verified: boolean\n  name: string\n  given_name: string\n  family_name: string\n  preferred_username: string\n  nickname: string\n  groups: string[]\n}\n\n/**\n * Add Authentik login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/authentik\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Authentik from \"@auth/core/providers/authentik\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Authentik({\n *       clientId: AUTHENTIK_CLIENT_ID,\n *       clientSecret: AUTHENTIK_CLIENT_SECRET,\n *       issuer: AUTHENTIK_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * :::note\n * issuer should include the slug without a trailing slash – e.g., https://my-authentik-domain.com/application/o/My_Slug\n * :::\n *\n * ### Resources\n *\n *  - [Authentik OAuth documentation](https://goauthentik.io/docs/providers/oauth2)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Authentik provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * The Authentik provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/authentik.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Authentik<P extends AuthentikProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"authentik\",\n    name: \"Authentik\",\n    type: \"oidc\",\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/azure-ad-b2c.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#0072c6\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Azure AD B2C</b> integration.</span>\n * <a href=\"https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/azure.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/azure-ad-b2c\n */\n\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\n/** @see [Claims](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tokens-overview#claims) */\nexport interface AzureADB2CProfile {\n  exp: number\n  nbf: number\n  ver: string\n  iss: string\n  sub: string\n  aud: string\n  iat: number\n  auth_time: number\n  oid: string\n  country: string\n  name: string\n  postalCode: string\n  emails: string[]\n  tfp: string\n  preferred_username: string\n}\n\n/**\n * Add Azure AD B2C login to your page.\n *\n *\n * ## Configuration\n *\n * ### Basic\n *\n * Basic configuration sets up Azure AD B2C to return an ID Token. This should be done as a prerequisite prior to running through the Advanced configuration.\n *\n * 1. [Azure AD B2C Tenant](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant)\n * 2. [App Registration](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-register-applications)\n * 3. [User Flow](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows)\n *\n * For the step \"User attributes and token claims\" set the following:\n *\n * - Collect attribute:\n *   - Email Address\n *   - Display Name\n *   - Given Name\n *   - Surname\n * - Return claim:\n *   - Email Addresses\n *   - Display Name\n *   - Given Name\n *   - Surname\n *   - Identity Provider\n *   - Identity Provider Access Token\n *   - User's Object ID\n *\n * @example\n *\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import AzureADB2C from \"@auth/core/providers/azure-ad-b2c\"\n *\n * const request = new Request(\"https://example.com\")\n * const response = await AuthHandler(request, {\n *   // optionally, you can pass `tenantId` and `primaryUserFlow` instead of `issuer`\n *   providers: [AzureADB2C({ clientId: \"\", clientSecret: \"\", issuer: \"\" })],\n * })\n * ```\n *\n * ---\n *\n * ### Resources\n *\n * - [Azure Active Directory B2C documentation](https://learn.microsoft.com/en-us/azure/active-directory-b2c)\n *\n * ---\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Azure AD B2C provider is\n * based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * The Azure AD B2C provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/azure-ad-b2c.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function AzureADB2C(\n  options: OIDCUserConfig<AzureADB2CProfile>\n): OIDCConfig<AzureADB2CProfile> {\n  return {\n    id: \"azure-ad-b2c\",\n    name: \"Azure AD B2C\",\n    type: \"oidc\",\n    profile(profile) {\n      return {\n        id: profile.sub,\n        name: profile.name ?? profile.preferred_username,\n        email: profile?.emails?.[0],\n        image: null,\n      }\n    },\n    style: { text: \"#fff\", bg: \"#0072c6\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/azure-ad.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#0072c6\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Azure AD</b> integration.</span>\n * <a href=\"https://learn.microsoft.com/en-us/azure/active-directory\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/azure-ad.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/azure-ad\n */\nimport MicrosoftEntraID, {\n  MicrosoftEntraIDProfile,\n} from \"./microsoft-entra-id.js\"\n\nexport type AzureADProfile = MicrosoftEntraIDProfile\n\n/**\n * @deprecated\n * Azure Active Directory has been renamed to [Microsoft Entra ID](/getting-started/providers/microsoft-entra-id).\n * Import this provider from the `providers/microsoft-entra-id` submodule instead of `providers/azure-ad`.\n */\nexport default function AzureAD(\n  config: Parameters<typeof MicrosoftEntraID>[0]\n): ReturnType<typeof MicrosoftEntraID> {\n  return {\n    ...MicrosoftEntraID(config),\n    id: \"azure-ad\",\n    name: \"Azure Active Directory\",\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/azure-devops.ts",
    "content": "import { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/** @see [Azure DevOps Services REST API 7.0 · Profiles · Get](https://learn.microsoft.com/en-us/rest/api/azure/devops/profile/profiles/get?view=azure-devops-rest-7.0&tabs=HTTP#examples) */\nexport interface AzureDevOpsProfile extends Record<string, any> {\n  id: string\n  displayName: string\n  emailAddress: string\n  coreAttributes: { Avatar: { value: { value: string } } }\n}\n\n/**\n *\n * @deprecated\n * While still available, Microsoft is [no longer supporting](https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops#available-oauth-models) Azure DevOps OAuth and recommends using [Microsoft Entra ID](/getting-started/providers/microsoft-entra-id) instead.\n *\n * ## Documentation\n *\n * [Microsoft Docs](https://docs.microsoft.com/en-us) · [Azure DevOps](https://docs.microsoft.com/en-us/azure/devops/) · [Authorize access to REST APIs with OAuth 2.0](https://docs.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops])\n *\n * ## Configuration\n *\n * ### Register application\n *\n * :::tip\n * [`https://app.vsaex.visualstudio.com/app/register`](https://app.vsaex.visualstudio.com/app/register)\n * :::\n *\n * Provide the required details:\n *\n * - Company name\n * - Application name\n * - Application website\n * - Authorization callback URL\n *   - `https://example.com/api/auth/callback/azure-devops` for production\n *   - `https://localhost/api/auth/callback/azure-devops` for development\n * - Authorized scopes\n *   - Required minimum is `User profile (read)`\n *\n * Click ‘Create Application’\n *\n * :::warning\n * You are required to use HTTPS even for the localhost\n * :::\n *\n * :::warning\n * You will have to delete and create a new application to change the scopes later\n * :::\n *\n * The following data is relevant for the next step:\n *\n * - App ID\n * - Client Secret (after clicking the ‘Show’ button, ignore App Secret entry above it)\n * - Authorized Scopes\n *\n * ### Set up the environment variables\n *\n * In `.env.local` create the following entries:\n *\n * ```\n * AZURE_DEVOPS_APP_ID=<copy App ID value here>\n * AZURE_DEVOPS_CLIENT_SECRET=<copy generated client secret value here>\n * AZURE_DEVOPS_SCOPE=<copy space separated Authorized Scopes list here>\n * ```\n *\n * ## Example\n *\n * ```ts\n * import AzureDevOps from \"@auth/core/providers/azure-devops\"\n * ...\n * providers: [\n *   AzureDevOps({\n *     clientId: process.env.AZURE_DEVOPS_APP_ID,\n *     clientSecret: process.env.AZURE_DEVOPS_CLIENT_SECRET,\n *     scope: process.env.AZURE_DEVOPS_SCOPE,\n *   }),\n * ]\n * ...\n * ```\n *\n * ### Refresh token rotation\n *\n * Use the [main guide](/guides/basics/refresh-token-rotation) as your starting point with the following considerations:\n *\n * ```ts\n * async jwt({ token, user, account }) {\n *   ...\n *   // The token has an absolute expiration time\n *   const accessTokenExpires = account.expires_at * 1000\n *   ...\n * }\n *\n * async function refreshAccessToken(token) {\n *   ...\n *   const response = await fetch(\n *     \"https://app.vssps.visualstudio.com/oauth2/token\",\n *     {\n *       headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n *       method: \"POST\",\n *       body: new URLSearchParams({\n *         client_assertion_type:\n *           \"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\",\n *         client_assertion: process.env.AZURE_DEVOPS_CLIENT_SECRET,\n *         grant_type: \"refresh_token\",\n *         assertion: token.refreshToken,\n *         redirect_uri:\n *           process.env.NEXTAUTH_URL + \"/api/auth/callback/azure-devops\",\n *       }),\n *     }\n *   )\n *   ...\n *   // The refreshed token comes with a relative expiration time\n *   const accessTokenExpires = Date.now() + newToken.expires_in * 1000\n *   ...\n * }\n * ```\n */\nexport default function AzureDevOpsProvider<P extends AzureDevOpsProfile>(\n  options: OAuthUserConfig<P> & {\n    /**\n     * https://docs.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops#scopes\n     * @default vso.profile\n     */\n    scope?: string\n  }\n): OAuthConfig<P> {\n  const scope = options.scope ?? \"vso.profile\"\n  const tokenEndpointUrl = \"https://app.vssps.visualstudio.com/oauth2/authorize\"\n  const userInfoEndpointUrl =\n    \"https://app.vssps.visualstudio.com/_apis/profile/profiles/me?details=true&coreAttributes=Avatar&api-version=6.0\"\n\n  return {\n    id: \"azure-devops\",\n    name: \"Azure DevOps\",\n    type: \"oauth\",\n\n    authorization: {\n      url: \"https://app.vssps.visualstudio.com/oauth2/authorize\",\n      params: { response_type: \"Assertion\", scope },\n    },\n\n    token: {\n      url: tokenEndpointUrl,\n      async request(context) {\n        const response = await fetch(tokenEndpointUrl, {\n          headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n          method: \"POST\",\n          body: new URLSearchParams({\n            client_assertion_type:\n              \"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\",\n            client_assertion: context.provider.clientSecret as string,\n            grant_type: \"urn:ietf:params:oauth:grant-type:jwt-bearer\",\n            assertion: context.params.code as string,\n            redirect_uri: context.provider.callbackUrl,\n          }),\n        })\n        return { tokens: await response.json() }\n      },\n    },\n\n    userinfo: {\n      url: userInfoEndpointUrl,\n      async request(context) {\n        const accessToken = context.tokens.access_token as string\n        const response = await fetch(userInfoEndpointUrl, {\n          headers: {\n            Authorization: `Bearer ${accessToken}`,\n          },\n        })\n        return response.json()\n      },\n    },\n\n    profile(profile) {\n      return {\n        id: profile.id,\n        name: profile.displayName,\n        email: profile.emailAddress,\n        image: `data:image/jpeg;base64,${profile.coreAttributes.Avatar.value.value}`,\n      }\n    },\n\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/bankid-no.ts",
    "content": "/**\n * <div class=\"provider\" style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\"}}>\n * <span style={{fontSize: \"1.35rem\" }}>\n *  Built-in sign in with <b>BankID Norway</b> integration.\n * </span>\n * <a href=\"https://bankid.no\" style={{backgroundColor: \"black\", padding: \"12px\", borderRadius: \"100%\" }}>\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/bankid-no.svg\" width=\"24\"/>\n * </a>\n * </div>\n *\n * @module providers/bankid-no\n */\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\n/**\n * @see [Core conepts - ID Token](https://confluence.bankidnorge.no/confluence/pdoidcl/technical-documentation/core-concepts/id-token)\n * @see [userinfo](https://confluence.bankidnorge.no/confluence/pdoidcl/technical-documentation/api/userinfo)\n */\nexport interface BankIDNorwayProfile {\n  exp: number\n  iat: number\n  /** Epoc time */\n  auth_time: number\n  jti: string\n  iss: string\n  /** Always client_id */\n  aud: string\n  sub: string\n  typ: \"ID\"\n  /** Equals client_id */\n  azp: string\n  session_state: string\n  at_hash: string\n  name: string\n  given_name: string\n  family_name: string\n  birthdate: string\n  updated_at: number\n  /**\n   * Uniform Resource Name for [IDP option](https://confluence.bankidnorge.no/confluence/pdoidcl/technical-documentation/core-concepts/identity-providers) being used,\n   * including Level of Assurance (LoA).\n   * @example\n   * ```\n   * urn:bankid:bid;LOA=4\n   * ```\n   */\n  acr: string\n  sid: string\n  /**\n   * Name of [IDP option](https://confluence.bankidnorge.no/confluence/pdoidcl/technical-documentation/core-concepts/identity-providers) being used to authenticate the end-user.\n   * If the end-user is subject to authentication step-up,\n   * note that this value may differ from any `amr` value specified\n   * in the `login_hint` parameter of the [authorize](https://confluence.bankidnorge.no/confluence/pdoidcl/technical-documentation/api/authorize) endpoint.\n   */\n  amr: \"BID\" | \"BIM\" | \"BIS\"\n  /** Personal Identifier (PID) / Serial Number) from associated BankID certificate. */\n  bankid_altsub: string\n  /**\n   * In case of BID or BIM, the issuer of the end user certificate is returned.\n   * @example\n   * ```\n   * CN=BankID Bankenes ID-tjeneste Bank CA 2,\n   * OU=988477052,\n   * O=Bankenes ID-tjeneste AS,*\n   * C=NO;OrginatorId=9775;OriginatorName=Gjensidige Bank RA 1\n   * ```\n   */\n  originator: string\n  additionalCertInfo: {\n    certValidFrom: number\n    serialNumber: string\n    keyAlgorithm: string\n    keySize: string\n    policyOid: string\n    certQualified: boolean\n    certValidTo: number\n    versionNumber: string\n    subjectName: string\n  }\n  /** Currently used as an input parameter for the [securityData](https://confluence.bankidnorge.no/confluence/pdoidcl/technical-documentation/api/securitydata) endpoint of the [Fraud Data](https://confluence.bankidnorge.no/confluence/pdoidcl/technical-documentation/advanced-topics/fraud-data) service */\n  tid: string\n  /** Only returned from the `userinfo_endpoint` */\n  email?: string\n  /**\n   * [Norwegian National Identity Number (fødselsnummer)](https://www.skatteetaten.no/en/person/foreign/norwegian-identification-number/national-identity-number). It can be an alternative to `sub`.\n   * Requires `nnin_altsub` scope at the [authorize](https://confluence.bankidnorge.no/confluence/pdoidcl/technical-documentation/api/authorize) endpoint.\n   * @example\n   * ```\n   * 181266*****\n   * ```\n   */\n  nnin_altsub?: string\n}\n\n/**\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/bankid-no\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import BankIDNorge from \"@auth/core/providers/bankid-no\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Auth0({\n *       clientId: AUTH_BANKID_NO_ID,\n *       clientSecret: AUTH_BANKID_NO_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [OpenID Connect Provider from BankID](https://confluence.bankidnorge.no/confluence/pdoidcl)\n *\n * ### Notes\n *\n * The BankID Norge provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/bankid-no.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * ## Help\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n */\nexport default function BankIDNorway(\n  config: OIDCUserConfig<BankIDNorwayProfile>\n): OIDCConfig<BankIDNorwayProfile> {\n  return {\n    id: \"bankid-no\",\n    name: \"BankID Norge\",\n    type: \"oidc\",\n    issuer: \"https://auth.bankid.no/auth/realms/prod\",\n    client: {\n      token_endpoint_auth_method: \"client_secret_post\",\n      userinfo_signed_response_alg: \"RS256\",\n    },\n    idToken: false,\n    authorization: { params: { ui_locales: \"no\", login_hint: \"BIS\" } },\n    profile(profile) {\n      return {\n        id: profile.sub,\n        name: profile.name,\n        email: profile.email ?? null,\n        image: null,\n      }\n    },\n    checks: [\"pkce\", \"state\", \"nonce\"],\n    style: { text: \"#fff\", bg: \"#39134c\" },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/battlenet.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Battle.net</b> integration.</span>\n * <a href=\"https://Battle.net/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/battlenet.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/battlenet\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface BattleNetProfile extends Record<string, any> {\n  sub: string\n  battle_tag: string\n}\n\n/** See the [available regions](https://develop.battle.net/documentation/guides/regionality-and-apis) */\nexport type BattleNetIssuer =\n  | \"https://oauth.battle.net\"\n  | \"https://oauth.battlenet.com.cn\"\n  | \"https://www.battlenet.com.cn/oauth\"\n  | `https://${\"us\" | \"eu\" | \"kr\" | \"tw\"}.battle.net/oauth`\n\n/**\n * Add Battle.net login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/battlenet\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import BattleNet from \"@auth/core/providers/battlenet\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     BattleNet({\n *       clientId: BATTLENET_CLIENT_ID,\n *       clientSecret: BATTLENET_CLIENT_SECRET,\n *       issuer: BATTLENET_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n * issuer must be one of these values, based on the available regions:\n * ```\n * type BattleNetIssuer =\n *   | \"https://oauth.battle.net\"\n *   | \"https://oauth.battlenet.com.cn\"\n *   | \"https://www.battlenet.com.cn/oauth\"\n *   | \"https://us.battle.net/oauth\"\n *   | \"https://eu.battle.net/oauth\"\n *   | \"https://kr.battle.net/oauth\"\n *   | \"https://tw.battle.net/oauth\"\n * ```\n *\n * ### Resources\n *\n *  - [BattleNet OAuth documentation](https://develop.battle.net/documentation/guides/using-oauth)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the BattleNet provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The BattleNet provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/battlenet.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function BattleNet<P extends BattleNetProfile>(\n  options: OAuthUserConfig<P> & { issuer: BattleNetIssuer }\n): OAuthConfig<P> {\n  return {\n    id: \"battlenet\",\n    name: \"Battle.net\",\n    type: \"oidc\",\n    profile(profile) {\n      return {\n        id: profile.sub,\n        name: profile.battle_tag,\n        email: null,\n        image: null,\n      }\n    },\n    style: { bg: \"#148eff\", text: \"#fff\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/beyondidentity.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#5077c5\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Beyond Identity</b> integration.</span>\n * <a href=\"https://www.beyondidentity.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/beyondidentity.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/beyondidentity\n */\n\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\n/** @see [Beyond Identity Developer Docs](https://developer.beyondidentity.com/) */\nexport interface BeyondIdentityProfile {\n  /** The user's unique identifier. */\n  sub: string\n  /** The user's full name. */\n  name: string\n  /** The user's preferred username. */\n  preferred_username: string\n  /** The user's email address. */\n  email: string\n}\n\n/**\n * Add Beyond Identity login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/beyondidentity\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import BeyondIdentity from \"@auth/core/providers/beyondidentity\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     BeyondIdentity({\n *       clientId: BEYOND_IDENTITY_CLIENT_ID,\n *       clientSecret: BEYOND_IDENTITY_CLIENT_SECRET,\n *       issuer: BEYOND_IDENTITY_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Beyond Identity Developer Docs](https://developer.beyondidentity.com/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the BeyondIdentity provider is\n * based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * The BeyondIdentity provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/beyondidentity.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\n\nexport default function BeyondIdentity(\n  config: OIDCUserConfig<BeyondIdentityProfile>\n): OIDCConfig<BeyondIdentityProfile> {\n  return {\n    id: \"beyondidentity\",\n    name: \"Beyond Identity\",\n    type: \"oidc\",\n    profile(profile) {\n      return {\n        id: profile.sub,\n        email: profile.email,\n        name: profile.name,\n        image: null,\n        preferred_username: profile.preferred_username,\n      }\n    },\n    style: {\n      bg: \"#5077c5\",\n      text: \"#fff\",\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/bitbucket.ts",
    "content": "/**\n * <div class=\"provider\" style={{ display: \"flex\", justifyContent: \"space-between\", color: \"#fff\" }}>\n * <span>Built-in <b>Bitbucket</b> integration.</span>\n * <a href=\"https://bitbucket.org\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/bitbucket.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/bitbucket\n */\n\nimport { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\ntype LiteralUnion<T extends U, U = string> = T | (U & Record<never, never>)\n\n/**\n * @see https://developer.atlassian.com/cloud/bitbucket/rest/api-group-users/#api-user-get\n */\nexport interface BitbucketProfile {\n  display_name: string\n  links: Record<\n    LiteralUnion<\n      \"self\" | \"avatar\" | \"repositories\" | \"snippets\" | \"html\" | \"hooks\"\n    >,\n    { href?: string }\n  >\n  created_on: string\n  type: string\n  uuid: string\n  has_2fa_enabled: boolean | null\n  username: string\n  is_staff: boolean\n  account_id: string\n  nickname: string\n  account_status: string\n  location: string | null\n}\n\n/**\n *\n * ### Setup\n *\n * #### Callback URL\n *\n * ```ts\n * https://example.com/api/auth/callback/bitbucket\n * ```\n *\n * #### Configuration\n *\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Bitbucket from \"@auth/core/providers/bitbucket\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Bitbucket({\n *       clientId: process.env.BITBUCKET_CLIENT_ID,\n *       clientSecret: process.env.BITBUCKET_CLIENT_SECRET,\n *     })\n *   ],\n * })\n * ```\n *\n * #### Resources\n *\n * - [Using OAuth on Bitbucket Cloud](https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/)\n * - [Bitbucket REST API Authentication](https://developer.atlassian.com/cloud/bitbucket/rest/intro/#authentication)\n * - [Bitbucket REST API Users](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-users/#api-group-users)\n *\n *  #### Notes\n *\n * By default, Auth.js assumes that the Bitbucket provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Bitbucket provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/bitbucket.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Bitbucket(\n  options: OAuthUserConfig<BitbucketProfile>\n): OAuthConfig<BitbucketProfile> {\n  return {\n    id: \"bitbucket\",\n    name: \"Bitbucket\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://bitbucket.org/site/oauth2/authorize\",\n      params: {\n        scope: \"account\",\n      },\n    },\n    token: \"https://bitbucket.org/site/oauth2/access_token\",\n    userinfo: \"https://api.bitbucket.org/2.0/user\",\n    profile(profile) {\n      return {\n        name: profile.display_name ?? profile.username,\n        id: profile.account_id,\n        image: profile.links.avatar?.href,\n      }\n    },\n    options,\n    style: {\n      text: \"#fff\",\n      bg: \"#205081\",\n    },\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/box.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Box</b> integration.</span>\n * <a href=\"https://box.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/box.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/box\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add Box login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/box\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Box from \"@auth/core/providers/box\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Box({ clientId: BOX_CLIENT_ID, clientSecret: BOX_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Box developers documentation](https://developer.box.com/reference/)\n *  - [Box OAuth documentation](https://developer.box.com/guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-box/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Box provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Box provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/box.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Box(\n  options: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"box\",\n    name: \"Box\",\n    type: \"oauth\",\n    authorization: \"https://account.box.com/api/oauth2/authorize\",\n    token: \"https://api.box.com/oauth2/token\",\n    userinfo: \"https://api.box.com/2.0/users/me\",\n    profile(profile) {\n      return {\n        id: profile.id,\n        name: profile.name,\n        email: profile.login,\n        image: profile.avatar_url,\n      }\n    },\n    style: {\n      bg: \"#0075C9\",\n      text: \"#fff\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/boxyhq-saml.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>BoxyHQ SAML</b> integration.</span>\n * <a href=\"https://boxyhq.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/boxyhq-saml.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/boxyhq-saml\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface BoxyHQSAMLProfile extends Record<string, any> {\n  id: string\n  email: string\n  firstName?: string\n  lastName?: string\n}\n\n/**\n * Add BoxyHQ SAML login to your page.\n *\n * BoxyHQ SAML is an open source service that handles the SAML SSO login flow as an OAuth 2.0 flow, abstracting away all the complexities of the SAML protocol. Enable Enterprise single-sign-on in your app with ease.\n *\n * You can deploy BoxyHQ SAML as a separate service or embed it into your app using our NPM library. [Check out the documentation for more details](https://boxyhq.com/docs/jackson/deploy)\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/boxyhq-saml\n * ```\n *\n * #### Configuration\n *\n * For OAuth 2.0 Flow:\n *```ts\n * import { Auth } from \"@auth/core\"\n * import BoxyHQ from \"@auth/core/providers/boxyhq-saml\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     BoxyHQ({\n *       authorization: { params: { scope: \"\" } }, // This is needed for OAuth 2.0 flow, otherwise default to openid\n *       clientId: BOXYHQ_SAML_CLIENT_ID,\n *       clientSecret: BOXYHQ_SAML_CLIENT_SECRET,\n *       issuer: BOXYHQ_SAML_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n * For OIDC Flow:\n *\n *```ts\n * import { Auth } from \"@auth/core\"\n * import BoxyHQ from \"@auth/core/providers/boxyhq-saml\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     BoxyHQ({\n *       clientId: BOXYHQ_SAML_CLIENT_ID,\n *       clientSecret: BOXYHQ_SAML_CLIENT_SECRET,\n *       issuer: BOXYHQ_SAML_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [BoxyHQ OAuth documentation](https://example.com)\n *\n * ## Configuration\n *\n * SAML login requires a configuration for every tenant of yours. One common method is to use the domain for an email address to figure out which tenant they belong to. You can also use a unique tenant ID (string) from your backend for this, typically some kind of account or organization ID.\n *\n * Check out the [documentation](https://boxyhq.com/docs/jackson/saml-flow#2-saml-config-api) for more details.\n *\n *\n * On the client side you'll need to pass additional parameters `tenant` and `product` to the `signIn` function. This will allow BoxyHQL SAML to figure out the right SAML configuration and take your user to the right SAML Identity Provider to sign them in.\n *\n * ```tsx\n * import { signIn } from \"auth\";\n * ...\n *\n *   // Map your users's email to a tenant and product\n *   const tenant = email.split(\"@\")[1];\n *   const product = 'my_awesome_product';\n * ...\n *   <Button\n *     onClick={async (event) => {\n *       event.preventDefault();\n *\n *       signIn(\"boxyhq-saml\", {}, { tenant, product });\n *     }}>\n * ...\n * ```\n * ### Notes\n *\n * By default, Auth.js assumes that the BoxyHQ provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The BoxyHQ provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/boxyhq-saml.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function SAMLJackson<P extends BoxyHQSAMLProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"boxyhq-saml\",\n    name: \"BoxyHQ SAML\",\n    type: \"oauth\",\n    authorization: {\n      url: `${options.issuer}/api/oauth/authorize`,\n      params: { provider: \"saml\" },\n    },\n    token: `${options.issuer}/api/oauth/token`,\n    userinfo: `${options.issuer}/api/oauth/userinfo`,\n    profile(profile) {\n      return {\n        id: profile.id,\n        email: profile.email,\n        name: [profile.firstName, profile.lastName].filter(Boolean).join(\" \"),\n        image: null,\n      }\n    },\n    style: {\n      brandColor: \"#25c2a0\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/bungie.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Bungie</b> integration.</span>\n * <a href=\"https://bungie.net/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/bungie.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/bungie\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add Bungie login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/bungie\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Bungie from \"@auth/core/providers/bungie\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Bungie({\n *       clientId: BUNGIE_CLIENT_ID,\n *       clientSecret: BUNGIE_CLIENT_SECRET,\n *       headers: { \"X-API-Key\": BUNGIE_API_KEY },\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Bungie OAuth documentation](https://github.com/Bungie-net/api/wiki/OAuth-Documentation)\n *\n * ## Configuration\n *\n * :::tip\n * Bungie require all sites to run HTTPS (including local development instances).\n * :::\n *\n * :::tip\n * Bungie doesn't allow you to use localhost as the website URL, instead you need to use https://127.0.0.1:3000\n * :::\n *\n * Navigate to https://www.bungie.net/en/Application and fill in the required details:\n *\n * - Application name\n * - Application Status\n * - Website\n * - OAuth Client Type\n *   - Confidential\n * - Redirect URL\n *   - https://localhost:3000/api/auth/callback/bungie\n * - Scope\n *   - `Access items like your Bungie.net notifications, memberships, and recent Bungie.Net forum activity.`\n * - Origin Header\n *\n * The following guide may be helpful:\n *\n * - [How to setup localhost with HTTPS with a Next.js app](https://medium.com/@anMagpie/secure-your-local-development-server-with-https-next-js-81ac6b8b3d68)\n *\n * #@example server\n *\n * You will need to edit your host file and point your site at `127.0.0.1`\n *\n * [How to edit my host file?](https://phoenixnap.com/kb/how-to-edit-hosts-file-in-windows-mac-or-linux)\n *\n * On Windows (Run PowerShell as administrator)\n *\n * ```ps\n * Add-Content -Path C:\\Windows\\System32\\drivers\\etc\\hosts -Value \"127.0.0.1`tdev.example.com\" -Force\n * ```\n *\n * ```\n * 127.0.0.1 dev.example.com\n * ```\n *\n * ### Create certificate\n *\n * Creating a certificate for localhost is easy with openssl. Just put the following command in the terminal. The output will be two files: localhost.key and localhost.crt.\n *\n * ```bash\n * openssl req -x509 -out localhost.crt -keyout localhost.key \\\n *   -newkey rsa:2048 -nodes -sha256 \\\n *   -subj \"/CN=localhost\" -extensions EXT -config <( \\\n *    printf \"[dn]\\nCN=localhost\\n[req]\\ndistinguished_name = dn\\n[EXT]\\nsubjectAltName=DNS:localhost\\nkeyUsage=digitalSignature\\nextendedKeyUsage=serverAuth\")\n * ```\n *\n * :::tip\n * **Windows**\n *\n * The OpenSSL executable is distributed with [Git](https://git-scm.com/download/win]9) for Windows.\n * Once installed you will find the openssl.exe file in `C:/Program Files/Git/mingw64/bin` which you can add to the system PATH environment variable if it’s not already done.\n *\n * Add environment variable `OPENSSL_CONF=C:/Program Files/Git/mingw64/ssl/openssl.cnf`\n *\n * ```bash\n *  req -x509 -out localhost.crt -keyout localhost.key \\\n *   -newkey rsa:2048 -nodes -sha256 \\\n *   -subj \"/CN=localhost\"\n * ```\n *\n * :::\n *\n * Create directory `certificates` and place `localhost.key` and `localhost.crt`\n *\n * You can create a `server.js` in the root of your project and run it with `node server.js` to test Sign in with Bungie integration locally:\n *\n * ```js\n * const { createServer } = require(\"https\")\n * const { parse } = require(\"url\")\n * const next = require(\"next\")\n * const fs = require(\"fs\")\n *\n * const dev = process.env.NODE_ENV !== \"production\"\n * const app = next({ dev })\n * const handle = app.getRequestHandler()\n *\n * const httpsOptions = {\n *   key: fs.readFileSync(\"./certificates/localhost.key\"),\n *   cert: fs.readFileSync(\"./certificates/localhost.crt\"),\n * }\n *\n * app.prepare().then(() => {\n *   createServer(httpsOptions, (req, res) => {\n *     const parsedUrl = parse(req.url, true)\n *     handle(req, res, parsedUrl)\n *   }).listen(3000, (err) => {\n *     if (err) throw err\n *     console.log(\"> Ready on https://localhost:3000\")\n *   })\n * })\n * ```\n *\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Bungie provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Bungie provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/bungie.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Bungie(\n  options: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"bungie\",\n    name: \"Bungie\",\n    type: \"oauth\",\n    authorization: \"https://www.bungie.net/en/OAuth/Authorize?reauth=true\",\n    token: \"https://www.bungie.net/platform/app/oauth/token/\",\n    userinfo:\n      \"https://www.bungie.net/platform/User/GetBungieAccount/{membershipId}/254/\",\n    profile(profile) {\n      const { bungieNetUser: user } = profile.Response\n\n      return {\n        id: user.membershipId,\n        name: user.displayName,\n        email: null,\n        image: `https://www.bungie.net${\n          user.profilePicturePath.startsWith(\"/\") ? \"\" : \"/\"\n        }${user.profilePicturePath}`,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/click-up.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#24292f\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>ClickUp</b> integration.</span>\n * <a href=\"https://clickup.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/click-up.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/click-up\n */\n\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/** @see [Get the authenticated user](https://clickup.com/api/clickupreference/operation/GetAuthorizedUser/)*/\nexport interface ClickUpProfile {\n  user: {\n    id: number\n    username: string\n    color: string\n    profilePicture: string\n  }\n}\n\n/**\n * Add ClickUp login to your page and make requests to [ClickUp APIs](https://clickup.com/api/).\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/clickup\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import ClickUp from \"@auth/core/providers/click-up\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     ClickUp({\n *       clientId: CLICKUP_CLIENT_ID,\n *       clientSecret: CLICKUP_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [ClickUp - Authorizing OAuth Apps](https://clickup.com/api/developer-portal/authentication#oauth-flow)\n * - [Source code](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/click-up.ts)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the ClickUp provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The ClickUp provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/click-up.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function ClickUp(\n  config: OAuthUserConfig<ClickUpProfile>\n): OAuthConfig<ClickUpProfile> {\n  return {\n    id: \"click-up\",\n    name: \"ClickUp\",\n    type: \"oauth\",\n    authorization: \"https://app.clickup.com/api\",\n    token: \"https://api.clickup.com/api/v2/oauth/token\",\n    userinfo: \"https://api.clickup.com/api/v2/user\",\n    clientId: config.clientId,\n    clientSecret: config.clientSecret,\n    checks: [\"state\"],\n    profile: (profile: ClickUpProfile) => {\n      return {\n        id: profile.user.id.toString(),\n        name: profile.user.username,\n        profilePicture: profile.user.profilePicture,\n        color: profile.user.color,\n      }\n    },\n    style: {\n      bg: \"#24292f\",\n      text: \"#fff\",\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/cognito.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Cognito</b> integration.</span>\n * <a href=\"https://docs.aws.amazon.com/cognito\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/cognito.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/cognito\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface CognitoProfile extends Record<string, any> {\n  sub: string\n  name: string\n  email: string\n  picture: string\n}\n\n/**\n * Add Cognito login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/cognito\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Cognito from \"@auth/core/providers/cognito\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Cognito({\n *       clientId: COGNITO_CLIENT_ID,\n *       clientSecret: COGNITO_CLIENT_SECRET,\n *       issuer: COGNITO_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Cognito OAuth documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-userpools-server-contract-reference.html)\n *\n * ### Notes\n * You need to select your AWS region to go the the Cognito dashboard.\n *\n * :::tip\n * The issuer is a URL, that looks like this: https://cognito-idp.{region}.amazonaws.com/{PoolId}\n * :::\n * `PoolId` is from General Settings in Cognito, not to be confused with the App Client ID.\n * :::warning\n * Make sure you select all the appropriate client settings or the OAuth flow will not work.\n * :::\n *\n * By default, Auth.js assumes that the Cognito provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * The Cognito provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/cognito.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Cognito<P extends CognitoProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"cognito\",\n    name: \"Cognito\",\n    type: \"oidc\",\n    style: {\n      brandColor: \"#C17B9E\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/coinbase.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Coinbase</b> integration.</span>\n * <a href=\"https://coinbase.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/coinbase.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/coinbase\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add Coinbase login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/coinbase\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Coinbase from \"@auth/core/providers/coinbase\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Coinbase({\n *       clientId: COINBASE_CLIENT_ID,\n *       clientSecret: COINBASE_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Coinbase OAuth documentation](https://developers.coinbase.com/api/v2)\n *\n * ### Notes\n *\n * :::tip\n * This Provider template has a 2 hour access token to it. A refresh token is also returned.\n * :::\n *\n * By default, Auth.js assumes that the Coinbase provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Coinbase provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/coinbase.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Coinbase(\n  options: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"coinbase\",\n    name: \"Coinbase\",\n    type: \"oauth\",\n    authorization:\n      \"https://login.coinbase.com/oauth2/auth?scope=wallet:user:email+wallet:user:read\",\n    token: \"https://login.coinbase.com/oauth2/token\",\n    userinfo: \"https://api.coinbase.com/v2/user\",\n    profile(profile) {\n      return {\n        id: profile.data.id,\n        name: profile.data.name,\n        email: profile.data.email,\n        image: profile.data.avatar_url,\n      }\n    },\n    style: {\n      brandColor: \"#0052ff\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/concept2.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Concept2</b> integration.</span>\n * <a href=\"https://concept2.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/concept2.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/concept2\n */\n\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface Concept2Profile extends Record<string, any> {\n  id: number\n  username: string\n  first_name: string\n  last_name: string\n  gender: string\n  dob: string\n  email: string\n  country: string\n  profile_image: string\n  age_restricted: boolean\n  email_permission: boolean | null\n  max_heart_rate: number | null\n  weight: number | null\n  logbook_privacy: string | null\n}\n\n/**\n * Add Concept2 login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/concept2\n * ```\n *\n * #### Configuration\n *```js\n * import { Auth } from \"@auth/core\"\n * import Concept2 from \"@auth/core/providers/concept2\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Concept2({\n *       clientId: CONCEPT2_CLIENT_ID,\n *       clientSecret: CONCEPT2_CLIENT_SECRET\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Concept2 OAuth documentation](https://log.concept2.com/developers/documentation/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Concept2 provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Concept2 provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/providers/concept2.ts)).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Concept2(\n  options: OAuthUserConfig<Concept2Profile>\n): OAuthConfig<Concept2Profile> {\n  return {\n    id: \"concept2\",\n    name: \"Concept2\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://log.concept2.com/oauth/authorize\",\n      params: {\n        scope: \"user:read,results:write\",\n      },\n    },\n    token: \"https://log.concept2.com/oauth/access_token\",\n    userinfo: \"https://log.concept2.com/api/users/me\",\n    profile(profile) {\n      return {\n        id: profile.data.id,\n        name: profile.data.username,\n        email: profile.data.email,\n        image: profile.data.profile_image,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/credentials.ts",
    "content": "import type { CommonProviderOptions } from \"./index.js\"\nimport type { Awaitable, User } from \"../types.js\"\nimport type { JSX } from \"preact\"\n\n/**\n * Besides providing type safety inside {@link CredentialsConfig.authorize}\n * it also determines how the credentials input fields will be rendered\n * on the default sign in page.\n */\nexport interface CredentialInput\n  extends Partial<JSX.IntrinsicElements[\"input\"]> {\n  label?: string\n}\n\n/** The Credentials Provider needs to be configured. */\nexport interface CredentialsConfig<\n  CredentialsInputs extends Record<string, CredentialInput> = Record<\n    string,\n    CredentialInput\n  >,\n> extends CommonProviderOptions {\n  type: \"credentials\"\n  credentials: CredentialsInputs\n  /**\n   * Gives full control over how you handle the credentials received from the user.\n   *\n   * :::warning\n   * There is no validation on the user inputs by default, so make sure you do so\n   * by a popular library like [Zod](https://zod.dev)\n   * :::\n   *\n   * This method expects a `User` object to be returned for a successful login.\n   *\n   * If an `CredentialsSignin` is thrown or `null` is returned, two things can happen:\n   * 1. The user is redirected to the login page, with `error=CredentialsSignin&code=credentials` in the URL. `code` is configurable, see below.\n   * 2. If you throw this error in a framework that handles form actions server-side, this error is thrown by the login form action, so you'll need to handle it there.\n   *\n   * In case of 1., generally, we recommend not hinting if the user for example gave a wrong username or password specifically,\n   * try rather something like \"invalid-credentials\". Try to be as generic with client-side errors as possible.\n   *\n   * To customize the error code, you can create a custom error that extends {@link CredentialsSignin} and throw it in `authorize`.\n   *\n   * @example\n   * ```ts\n   * class CustomError extends CredentialsSignin {\n   *  code = \"custom_error\"\n   * }\n   * // URL will contain `error=CredentialsSignin&code=custom_error`\n   * ```\n   *\n   * @example\n   * ```ts\n   * async authorize(credentials, request) { // you have access to the original request as well\n   *   if(!isValidCredentials(credentials)) {\n   *      throw new CustomError()\n   *   }\n   *   return await getUser(credentials) // assuming it returns a User or null\n   * }\n   * ```\n   */\n  authorize: (\n    /**\n     * The available keys are determined by {@link CredentialInput}.\n     *\n     * @note The existence/correctness of a field cannot be guaranteed at compile time,\n     * so you should always validate the input before using it.\n     *\n     * You can add basic validation depending on your use case,\n     * or you can use a popular library like [Zod](https://zod.dev) for example.\n     */\n    credentials: Partial<Record<keyof CredentialsInputs, unknown>>,\n    /** The original request. */\n    request: Request\n  ) => Awaitable<User | null>\n}\n\nexport type CredentialsProviderId = \"credentials\"\n\n/**\n * The Credentials provider allows you to handle signing in with arbitrary credentials,\n * such as a username and password, domain, or two factor authentication or hardware device (e.g. YubiKey U2F / FIDO).\n *\n * It is intended to support use cases where you have an existing system you need to authenticate users against.\n *\n * It comes with the constraint that users authenticated in this manner are not persisted in the database,\n * and consequently that the Credentials provider can only be used if JSON Web Tokens are enabled for sessions.\n *\n * :::caution\n * The functionality provided for credentials-based authentication is intentionally limited to discourage the use of passwords due to the inherent security risks of the username-password model.\n *\n * OAuth providers spend significant amounts of money, time, and engineering effort to build:\n *\n * - abuse detection (bot-protection, rate-limiting)\n * - password management (password reset, credential stuffing, rotation)\n * - data security (encryption/salting, strength validation)\n *\n * and much more for authentication solutions. It is likely that your application would benefit from leveraging these battle-tested solutions rather than try to rebuild them from scratch.\n *\n * If you'd still like to build password-based authentication for your application despite these risks, Auth.js gives you full control to do so.\n *\n * :::\n *\n * See the [callbacks documentation](/reference/core#authconfig#callbacks) for more information on how to interact with the token. For example, you can add additional information to the token by returning an object from the `jwt()` callback:\n *\n * ```ts\n * callbacks: {\n *   async jwt({ token, user, account, profile, isNewUser }) {\n *     if (user) {\n *       token.id = user.id\n *     }\n *     return token\n *   }\n * }\n * ```\n *\n * @example\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Credentials from \"@auth/core/providers/credentials\"\n *\n * const request = new Request(\"https://example.com\")\n * const response = await AuthHandler(request, {\n *   providers: [\n *     Credentials({\n *       credentials: {\n *         username: { label: \"Username\" },\n *         password: {  label: \"Password\", type: \"password\" }\n *       },\n *       async authorize({ request }) {\n *         const response = await fetch(request)\n *         if(!response.ok) return null\n *         return await response.json() ?? null\n *       }\n *     })\n *   ],\n *   secret: \"...\",\n *   trustHost: true,\n * })\n * ```\n * @see [Username/Password Example](https://authjs.dev/getting-started/authentication/credentials)\n */\nexport default function Credentials<\n  CredentialsInputs extends Record<string, CredentialInput> = Record<\n    string,\n    CredentialInput\n  >,\n>(config: Partial<CredentialsConfig<CredentialsInputs>>): CredentialsConfig {\n  return {\n    id: \"credentials\",\n    name: \"Credentials\",\n    type: \"credentials\",\n    credentials: {},\n    authorize: () => null,\n    // @ts-expect-error\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/descope.ts",
    "content": "/**\n * <div class=\"provider\" style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\"}}>\n * <span style={{fontSize: \"1.35rem\" }}>\n *  Built-in sign in with <b>Descope</b> integration.\n * </span>\n * <a href=\"https://descope.com\" style={{backgroundColor: \"#000000\", padding: \"12px\", borderRadius: \"100%\" }}>\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/descope.svg\" width=\"24\"/>\n * </a>\n * </div>\n *\n * @module providers/descope\n */\n\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\n/** The returned user profile from Descope when using the profile callback.\n * [See Load User](https://docs.descope.com/api/openapi/usermanagement/operation/LoadUser/)\n */\nexport interface DescopeProfile {\n  /** The user's unique Descope ID */\n  sub: string\n  /** The user's name */\n  name: string\n  /** The user's email */\n  email: string\n  /** A boolean indicating if the user's email is verified */\n  email_verified: boolean\n  /** The user's phone number */\n  phone_number: string\n  /** A boolean indicating if the user's phone number is verified */\n  phone_number_verified: boolean\n  /** The user's picture */\n  picture: string\n  /** The user's custom attributes */\n  [claim: string]: unknown\n}\n\n/**\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/descope\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Descope from \"@auth/core/providers/descope\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, { providers: [Descope({ clientId: AUTH_DESCOPE_ID, clientSecret: AUTH_DESCOPE_SECRET, issuer: AUTH_DESCOPE_ISSUER })] })\n * ```\n *\n * ### Configuring Descope\n *\n * Follow these steps:\n *\n * 1. Log into the [Descope console](https://app.descope.com)\n * 2. Follow the [OIDC instructions](https://docs.descope.com/customize/auth/oidc)\n *\n * Then, create a `.env.local` file in the project root add the following entries:\n *\n * Get the following from the Descope's console:\n * ```\n * AUTH_DESCOPE_ID=\"<Descope Issuer's last url segment>\" # Descope's Issuer can be found in \"Authentication Methods > SSO > Identity Provider\" (Can also be taken from \"Project > Project ID\")\n * AUTH_DESCOPE_SECRET=\"<Descope Access Key>\" # Manage > Access Keys\n * AUTH_DESCOPE_ISSUER=\"<Descope Issuer URL>\" # Applications -> OIDC Application -> Issuer\n * ```\n *\n * ### Resources\n *\n * - [Descope OIDC](https://docs.descope.com/customize/auth/oidc)\n * - [Descope Flows](https://docs.descope.com/customize/flows)\n *\n * ### Notes\n *\n * The Descope provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/descope.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::info\n * By default, Auth.js assumes that the Descope provider is based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) spec\n * :::\n *\n * ## Help\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n */\nexport default function Descope(\n  config: OIDCUserConfig<DescopeProfile>\n): OIDCConfig<DescopeProfile> {\n  config.issuer ??= `https://api.descope.com/${config.clientId}`\n  return {\n    id: \"descope\",\n    name: \"Descope\",\n    type: \"oidc\",\n    style: { bg: \"#1C1C23\", text: \"#ffffff\" },\n    checks: [\"pkce\", \"state\"],\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/discord.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Discord</b> integration.</span>\n * <a href=\"https://discord.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/discord.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/discord\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Corresponds to the user structure documented here:\n * https://discord.com/developers/docs/resources/user#user-object-user-structure\n */\nexport interface DiscordProfile extends Record<string, any> {\n  /** the user's id (i.e. the numerical snowflake) */\n  id: string\n  /** the user's username, not unique across the platform */\n  username: string\n  /** the user's Discord-tag */\n  discriminator: string\n  /** the user's display name, if it is set  */\n  global_name: string | null\n  /**\n   * the user's avatar hash:\n   * https://discord.com/developers/docs/reference#image-formatting\n   */\n  avatar: string | null\n  /** whether the user belongs to an OAuth2 application */\n  bot?: boolean\n  /**\n   * whether the user is an Official Discord System user (part of the urgent\n   * message system)\n   */\n  system?: boolean\n  /** whether the user has two factor enabled on their account */\n  mfa_enabled: boolean\n  /**\n   * the user's banner hash:\n   * https://discord.com/developers/docs/reference#image-formatting\n   */\n  banner: string | null\n\n  /** the user's banner color encoded as an integer representation of hexadecimal color code */\n  accent_color: number | null\n\n  /**\n   * the user's chosen language option:\n   * https://discord.com/developers/docs/reference#locales\n   */\n  locale: string\n  /** whether the email on this account has been verified */\n  verified: boolean\n  /** the user's email */\n  email: string | null\n  /**\n   * the flags on a user's account:\n   * https://discord.com/developers/docs/resources/user#user-object-user-flags\n   */\n  flags: number\n  /**\n   * the type of Nitro subscription on a user's account:\n   * https://discord.com/developers/docs/resources/user#user-object-premium-types\n   */\n  premium_type: number\n  /**\n   * the public flags on a user's account:\n   * https://discord.com/developers/docs/resources/user#user-object-user-flags\n   */\n  public_flags: number\n  /** undocumented field; corresponds to the user's custom nickname */\n  display_name: string | null\n  /**\n   * undocumented field; corresponds to the Discord feature where you can e.g.\n   * put your avatar inside of an ice cube\n   */\n  avatar_decoration: string | null\n  /**\n   * undocumented field; corresponds to the premium feature where you can\n   * select a custom banner color\n   */\n  banner_color: string | null\n  /** undocumented field; the CDN URL of their profile picture */\n  image_url: string\n}\n\n/**\n * Add Discord login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/discord\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Discord from \"@auth/core/providers/discord\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Discord({\n *       clientId: DISCORD_CLIENT_ID,\n *       clientSecret: DISCORD_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Discord OAuth documentation](https://discord.com/developers/docs/topics/oauth2)\n *  - [Discord OAuth apps](https://discord.com/developers/applications)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Discord provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Discord provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/discord.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Discord<P extends DiscordProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"discord\",\n    name: \"Discord\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://discord.com/api/oauth2/authorize\",\n      params: { scope: \"identify email\" },\n    },\n    token: \"https://discord.com/api/oauth2/token\",\n    userinfo: \"https://discord.com/api/users/@me\",\n    profile(profile) {\n      if (profile.avatar === null) {\n        const defaultAvatarNumber =\n          profile.discriminator === \"0\"\n            ? Number(BigInt(profile.id) >> BigInt(22)) % 6\n            : parseInt(profile.discriminator) % 5\n        profile.image_url = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarNumber}.png`\n      } else {\n        const format = profile.avatar.startsWith(\"a_\") ? \"gif\" : \"png\"\n        profile.image_url = `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.${format}`\n      }\n      return {\n        id: profile.id,\n        name: profile.global_name ?? profile.username,\n        email: profile.email,\n        image: profile.image_url,\n      }\n    },\n    style: { bg: \"#5865F2\", text: \"#fff\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/dribbble.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Dribbble</b> integration.</span>\n * <a href=\"https://dribbble.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/dribbble.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/dribbble\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface DribbbleProfile extends Record<string, any> {\n  id: number\n  name: string\n  email: string\n  avatar_url: string\n}\n\n/**\n *\n * Add Dribbble login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/dribbble\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Dribbble from \"@auth/core/providers/dribbble\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Dribbble({\n *       clientId: DRIBBBLE_CLIENT_ID,\n *       clientSecret: DRIBBBLE_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Dribbble API](https://developer.dribbble.com)\n *  - [Dribbble OAuth](https://developer.dribbble.com/v2/oauth/)\n *  - [Dribbble Applications](https://dribbble.com/account/applications/new)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the GitHub provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Dribbble provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/dribbble.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n *\n * :::tip\n * You can optionally set the scope to `public upload` for more advanced scenarios. If omitted, the default `public` scope will be used for authentication purposes.\n * :::\n */\n\nexport default function Dribbble<P extends DribbbleProfile>(\n  options: OAuthUserConfig<P> & {\n    /**\n     * Reference: https://developer.dribbble.com/v2/oauth/#scopes\n     *\n     * For the purposes of NextAuth.js `upload`-only scope makes no sense,\n     * therefore it is excluded from suggested values. Treated by Dribbble as `public` when omitted.\n     *\n     * @default public\n     */\n    scope?: \"public\" | \"public upload\"\n  }\n): OAuthConfig<P> {\n  return {\n    id: \"dribbble\",\n    name: \"Dribbble\",\n    type: \"oauth\",\n\n    authorization: {\n      url: \"https://dribbble.com/oauth/authorize\",\n      params: { scope: options.scope },\n    },\n\n    token: \"https://dribbble.com/oauth/token\",\n    userinfo: \"https://api.dribbble.com/v2/user\",\n\n    profile(profile) {\n      return {\n        id: profile.id.toString(),\n        name: profile.name,\n        email: profile.email,\n        image: profile.avatar_url,\n      }\n    },\n\n    style: {\n      text: \"#fff\",\n      bg: \"#000\",\n    },\n\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/dropbox.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Dropbox</b> integration.</span>\n * <a href=\"https://dropbox.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/dropbox.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/dropbox\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add Dropbox login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/dropbox\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Dropbox from \"@auth/core/providers/dropbox\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Dropbox({\n *       clientId: DROPBOX_CLIENT_ID,\n *       clientSecret: DROPBOX_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Dropbox OAuth documentation](https://developers.dropbox.com/oauth-guide)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Dropbox provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Dropbox provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/dropbox.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Dropbox(\n  options: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"dropbox\",\n    name: \"Dropbox\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://www.dropbox.com/oauth2/authorize\",\n      params: {\n        token_access_type: \"offline\",\n        scope: \"account_info.read\",\n      },\n    },\n    token: \"https://api.dropboxapi.com/oauth2/token\",\n    userinfo: {\n      url: \"https://api.dropboxapi.com/2/users/get_current_account\",\n      async request({ tokens, provider }) {\n        return await fetch(provider.userinfo?.url as URL, {\n          method: \"POST\",\n          headers: {\n            Authorization: `Bearer ${tokens.access_token}`,\n          },\n        }).then(async (res) => await res.json())\n      },\n    },\n    profile(profile) {\n      return {\n        id: profile.account_id,\n        name: profile.name.display_name,\n        email: profile.email,\n        image: profile.profile_photo_url,\n      }\n    },\n    style: { brandColor: \"#0061fe\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/duende-identity-server6.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>DuendeIdentityServer6</b> integration.</span>\n * <a href=\"https://docs.duendesoftware.com/identityserver/v6\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/duende-identity-server6.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/duende-identity-server6\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./oauth.js\"\n\nexport interface DuendeISUser extends Record<string, any> {\n  email: string\n  id: string\n  name: string\n  verified: boolean\n}\n\n/**\n * Add DuendeIdentityServer6 login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/duende-identity-server6\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import DuendeIdentityServer6 from \"@auth/core/providers/duende-identity-server6\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     DuendeIdentityServer6({\n *       clientId: DIS6_CLIENT_ID,\n *       clientSecret: DIS6_CLIENT_SECRET,\n *       issuer: DIS6_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [DuendeIdentityServer6 documentation](https://docs.duendesoftware.com/identityserver/v6)\n *\n * ### Notes\n *\n *\n * ## Demo IdentityServer\n *\n * The configuration below is for the demo server at https://demo.duendesoftware.com/\n *\n * If you want to try it out, you can copy and paste the configuration below.\n *\n * You can sign in to the demo service with either <b>bob/bob</b> or <b>alice/alice</b>.\n *\n * ```ts\n * import DuendeIdentityServer6 from \"@auth/core/providers/duende-identity-server6\"\n * providers: [\n *   DuendeIdentityServer6({\n *     clientId: \"interactive.confidential\",\n *     clientSecret: \"secret\",\n *     issuer: \"https://demo.duendesoftware.com\",\n *   })\n * ]\n * ```\n * By default, Auth.js assumes that the DuendeIdentityServer6 provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * The DuendeIdentityServer6 provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/duende-identity-server6.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function DuendeIdentityServer6<P extends DuendeISUser>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"duende-identity-server6\",\n    name: \"DuendeIdentityServer6\",\n    type: \"oidc\",\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/email.ts",
    "content": "import type { CommonProviderOptions } from \"./index.js\"\nimport type { Awaitable, Theme } from \"../types.js\"\nexport type { EmailProviderId } from \"./provider-types.js\"\n\n// TODO: Kepts for backwards compatibility\n// Remove this import and encourage users\n// to import it from @auth/core/providers/nodemailer directly\nimport Nodemailer from \"./nodemailer.js\"\nimport type { NodemailerConfig, NodemailerUserConfig } from \"./nodemailer.js\"\n\n/**\n * @deprecated\n *\n * Import this provider from the `providers/nodemailer` submodule instead of `providers/email`.\n *\n * To log in with nodemailer, change `signIn(\"email\")` to `signIn(\"nodemailer\")`\n */\nexport default function Email(config: NodemailerUserConfig): NodemailerConfig {\n  return {\n    ...Nodemailer(config),\n    id: \"email\",\n    name: \"Email\",\n  }\n}\n\n// TODO: Rename to Token provider\n// when started working on https://github.com/nextauthjs/next-auth/discussions/1465\nexport type EmailProviderType = \"email\"\n\nexport type EmailProviderSendVerificationRequestParams = {\n  identifier: string\n  url: string\n  expires: Date\n  provider: EmailConfig\n  token: string\n  theme: Theme\n  request: Request\n}\n\nexport interface EmailConfig extends CommonProviderOptions {\n  id: string\n  type: \"email\"\n  name: string\n  from?: string\n  maxAge?: number\n  sendVerificationRequest: (\n    params: EmailProviderSendVerificationRequestParams\n  ) => Awaitable<void>\n  /** Used to hash the verification token. */\n  secret?: string\n  /** Used with HTTP-based email providers. */\n  apiKey?: string\n  /** Used with SMTP-based email providers. */\n  server?: NodemailerConfig[\"server\"]\n  generateVerificationToken?: () => Awaitable<string>\n  normalizeIdentifier?: (identifier: string) => string\n  options?: EmailUserConfig\n}\n\nexport type EmailUserConfig = Omit<Partial<EmailConfig>, \"options\" | \"type\">\n"
  },
  {
    "path": "packages/core/src/providers/eventbrite.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#f05537\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Eventbrite</b> integration.</span>\n * <a href=\"https://www.eventbrite.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/eventbrite.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/eventbrite\n */\n\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * @see https://www.eventbrite.com/platform/api#/reference/user/retrieve-your-user/retrieve-your-user\n */\nexport interface EventbriteProfile extends Record<string, any> {\n  id: string\n  name: string\n  first_name: string\n  last_name: string\n  emails: { email: string; verified: boolean; primary: boolean }[]\n  image_id: string\n}\n\n/**\n * Add Eventbrite login to your page and make requests to [Eventbrite APIs](https://www.eventbrite.com/platform/api).\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/eventbrite\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Eventbrite from \"@auth/core/providers/eventbrite\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [Eventbrite({ clientId: EVENTBRITE_CLIENT_ID, clientSecret: EVENTBRITE_CLIENT_SECRET })],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Eventbrite OAuth documentation](https://www.eventbrite.com/platform/api#/introduction/authentication)\n * - [Eventbrite App Management](https://www.eventbrite.com/account-settings/apps)\n * - [Learn more about OAuth](https://authjs.dev/concepts/oauth)\n * - [Source code](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/eventbrite.ts)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Eventbrite provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Eventbrite provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/eventbrite.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Eventbrite<P extends EventbriteProfile>(\n  config: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"eventbrite\",\n    name: \"Eventbrite\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://www.eventbrite.com/oauth/authorize\",\n      params: { scope: \"user.profile\" },\n    },\n    token: \"https://www.eventbrite.com/oauth/token\",\n    userinfo: \"https://www.eventbriteapi.com/v3/users/me/\",\n    profile(profile) {\n      return {\n        id: profile.id,\n        name: profile.name,\n        email: profile.emails.find((e) => e.primary)?.email,\n        image: profile.image_id\n          ? `https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.com%2Fimages%2F${profile.image_id}%2F1%2Foriginal.jpg`\n          : null,\n      }\n    },\n    client: {\n      token_endpoint_auth_method: \"client_secret_post\",\n    },\n    style: { bg: \"#f05537\", text: \"#fff\" },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/eveonline.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>EVEOnline</b> integration.</span>\n * <a href=\"https://eveonline.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/eveonline.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/eveonline\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface EVEOnlineProfile extends Record<string, any> {\n  CharacterID: number\n  CharacterName: string\n  ExpiresOn: string\n  Scopes: string\n  TokenType: string\n  CharacterOwnerHash: string\n  IntellectualProperty: string\n}\n\n/**\n * Add EveOnline login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/eveonline\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import EveOnline from \"@auth/core/providers/eveonline\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     EveOnline({\n *       clientId: EVEONLINE_CLIENT_ID,\n *       clientSecret: EVEONLINE_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [EveOnline OAuth documentation](https://developers.eveonline.com/blog/article/sso-to-authenticated-calls)\n *\n * ### Notes\n *\n * :::tip\n * When creating your application, make sure to select `Authentication Only` as the connection type.\n * :::\n *\n * :::tip\n * If using JWT for the session, you can add the `CharacterID` to the JWT and session. Example:\n * ```ts\n * options: {\n *   jwt: {\n *     secret: process.env.JWT_SECRET,\n *   },\n *   callbacks: {\n *     session: async ({ session, token }) => {\n *       session.user.id = token.id;\n *       return session;\n *     }\n *   }\n * }\n * ```\n * :::\n * By default, Auth.js assumes that the EveOnline provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The EveOnline provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/eveonline.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function EVEOnline<P extends EVEOnlineProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"eveonline\",\n    name: \"EVE Online\",\n    type: \"oauth\",\n    authorization:\n      \"https://login.eveonline.com/v2/oauth/authorize?scope=publicData\",\n    token: \"https://login.eveonline.com/v2/oauth/token\",\n    userinfo: \"https://login.eveonline.com/oauth/verify\",\n    checks: [\"state\"],\n    profile(profile) {\n      return {\n        id: String(profile.CharacterID),\n        name: profile.CharacterName,\n        email: null,\n        image: `https://image.eveonline.com/Character/${profile.CharacterID}_128.jpg`,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/facebook.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Facebook</b> integration.</span>\n * <a href=\"https://facebook.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/facebook.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/facebook\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\ninterface FacebookPictureData {\n  url: string\n}\n\ninterface FacebookPicture {\n  data: FacebookPictureData\n}\nexport interface FacebookProfile extends Record<string, any> {\n  id: string\n  picture: FacebookPicture\n}\n\n/**\n * Add Facebook login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/facebook\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Facebook from \"@auth/core/providers/facebook\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Facebook({\n *       clientId: FACEBOOK_CLIENT_ID,\n *       clientSecret: FACEBOOK_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Facebook OAuth documentation](https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/)\n *\n * ### Notes\n *\n * :::tip\n * Production applications cannot use localhost URLs to sign in with Facebook. You need to use a dedicated development application in Facebook to use localhost callback URLs.\n * :::\n *\n * :::tip\n * Email address may not be returned for accounts created on mobile.\n * :::\n *\n * By default, Auth.js assumes that the Facebook provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Facebook provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/facebook.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Facebook<P extends FacebookProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"facebook\",\n    name: \"Facebook\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://www.facebook.com/v19.0/dialog/oauth\",\n      params: {\n        scope: \"email\",\n      },\n    },\n    token: \"https://graph.facebook.com/oauth/access_token\",\n    userinfo: {\n      // https://developers.facebook.com/docs/graph-api/reference/user/#fields\n      url: \"https://graph.facebook.com/me?fields=id,name,email,picture\",\n      async request({ tokens, provider }) {\n        return await fetch(provider.userinfo?.url as URL, {\n          headers: { Authorization: `Bearer ${tokens.access_token}` },\n        }).then(async (res) => await res.json())\n      },\n    },\n    profile(profile: P) {\n      return {\n        id: profile.id,\n        name: profile.name,\n        email: profile.email,\n        image: profile.picture.data.url,\n      }\n    },\n    style: { bg: \"#006aff\", text: \"#fff\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/faceit.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>FACEIT</b> integration.</span>\n * <a href=\"https://faceit.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/faceit.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/faceit\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add FACEIT login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/faceit\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import FACEIT from \"@auth/core/providers/faceit\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     FACEIT({ clientId: FACEIT_CLIENT_ID, clientSecret: FACEIT_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [FACEIT OAuth documentation](https://cdn.faceit.com/third_party/docs/FACEIT_Connect_3.0.pdf)\n *\n * ### Notes\n *\n * Grant type: Authorization Code\n * Scopes to have basic infos (email, nickname, guid and avatar) : openid, email, profile\n * By default, Auth.js assumes that the FACEIT provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The FACEIT provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/faceit.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function FACEIT(\n  options: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"faceit\",\n    name: \"FACEIT\",\n    type: \"oauth\",\n    authorization: \"https://accounts.faceit.com/accounts?redirect_popup=true\",\n    // @ts-expect-error - TODO fix this\n    headers: {\n      Authorization: `Basic ${Buffer.from(\n        `${options.clientId}:${options.clientSecret}`\n      ).toString(\"base64\")}`,\n    },\n    token: \"https://api.faceit.com/auth/v1/oauth/token\",\n    userinfo: \"https://api.faceit.com/auth/v1/resources/userinfo\",\n    profile(profile) {\n      return {\n        id: profile.guid,\n        name: profile.name,\n        email: profile.email,\n        image: profile.picture,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/figma.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Figma</b> integration.</span>\n * <a href=\"https://figma.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/figma.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/figma\n */\nimport { OAuth2Config, OAuthUserConfig } from \"./index.js\"\n\n/**\n * @see https://www.figma.com/developers/api#users-types\n */\ninterface FigmaProfile {\n  id: string\n  email: string\n  handle: string\n  img_url: string\n}\n\n/**\n * ### Setup\n *\n * #### Callback URL\n *\n * ```ts\n * https://example.com/api/auth/callback/figma\n * ```\n *\n * #### Configuration\n *\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Figma from \"@auth/core/providers/figma\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Figma({\n *       clientId: process.env.AUTH_FIGMA_ID,\n *       clientSecret: process.env.AUTH_FIGMA_SECRET\n *     })\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Using OAuth 2 on Figma](https://www.figma.com/developers/api#oauth2)\n * - [Scopes](https://www.figma.com/developers/api#authentication-scopes)\n *\n * #### Notes\n *\n * By default, Auth.js assumes that the Figma provider is based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Figma provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/figma.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Figma(\n  options: OAuthUserConfig<FigmaProfile>\n): OAuth2Config<FigmaProfile> {\n  return {\n    id: \"figma\",\n    name: \"Figma\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://www.figma.com/oauth\",\n      params: {\n        scope: \"files:read\",\n      },\n    },\n    checks: [\"state\"],\n    token: \"https://api.figma.com/v1/oauth/token\",\n    userinfo: \"https://api.figma.com/v1/me\",\n    profile(profile) {\n      return {\n        name: profile.handle,\n        email: profile.email,\n        id: profile.id,\n        image: profile.img_url,\n      }\n    },\n    style: {\n      text: \"#fff\",\n      bg: \"#ff7237\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/forwardemail.ts",
    "content": "import type { EmailConfig, EmailUserConfig } from \"./index.js\"\nimport { html, text } from \"../lib/utils/email.js\"\n\n/** @todo Document this */\nexport default function ForwardEmail(config: EmailUserConfig): EmailConfig {\n  return {\n    id: \"forwardemail\",\n    type: \"email\",\n    name: \"Forward Email\",\n    from: \"Auth.js <no-reply@authjs.dev>\",\n    maxAge: 24 * 60 * 60,\n    async sendVerificationRequest(params) {\n      const { identifier: to, provider, url, theme } = params\n      const { host } = new URL(url)\n      const res = await fetch(\"https://api.forwardemail.net/v1/emails\", {\n        method: \"POST\",\n        headers: {\n          Authorization: `Basic ${btoa(provider.apiKey + \":\")}`,\n          \"Content-Type\": \"application/json\",\n        },\n        body: JSON.stringify({\n          from: provider.from,\n          to,\n          subject: `Sign in to ${host}`,\n          html: html({ url, host, theme }),\n          text: text({ url, host }),\n        }),\n      })\n\n      if (!res.ok)\n        throw new Error(\n          \"Forward Email error: \" + JSON.stringify(await res.json())\n        )\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/foursquare.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>FourSquare</b> integration.</span>\n * <a href=\"https://foursquare.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/foursquare.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/foursquare\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add FourSquare login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/foursquare\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import FourSquare from \"@auth/core/providers/foursquare\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     FourSquare({\n *       clientId: FOURSQUARE_CLIENT_ID,\n *       clientSecret: FOURSQUARE_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [FourSquare OAuth documentation](https://docs.foursquare.com/developer/reference/authentication)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the FourSquare provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::warning\n * Foursquare requires an additional apiVersion parameter in YYYYMMDD format, which essentially states \"I'm prepared for API changes up to this date\".\n * :::\n *\n * :::tip\n *\n * The FourSquare provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/foursquare.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Foursquare(\n  options: OAuthUserConfig<Record<string, any>> & { apiVersion?: string }\n): OAuthConfig<Record<string, any>> {\n  const { apiVersion = \"20230131\" } = options\n  return {\n    id: \"foursquare\",\n    name: \"Foursquare\",\n    type: \"oauth\",\n    authorization: \"https://foursquare.com/oauth2/authenticate\",\n    token: \"https://foursquare.com/oauth2/access_token\",\n    userinfo: {\n      url: `https://api.foursquare.com/v2/users/self?v=${apiVersion}`,\n      async request({ tokens, provider }) {\n        if (!provider.userinfo) return\n\n        const url = new URL(provider.userinfo.url)\n        url.searchParams.append(\"oauth_token\", tokens.access_token!)\n        return fetch(url).then((res) => res.json())\n      },\n    },\n    profile({ response: { user: profile } }) {\n      return {\n        id: profile.id,\n        name: `${profile.firstName} ${profile.lastName}`,\n        email: profile.contact.email,\n        image: profile.photo\n          ? `${profile.photo.prefix}original${profile.photo.suffix}`\n          : null,\n      }\n    },\n    style: {\n      bg: \"#000\",\n      text: \"#fff\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/freshbooks.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>FreshBooks</b> integration.</span>\n * <a href=\"https://freshbooks.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/freshbooks.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/freshbooks\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add FreshBooks login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/freshbooks\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import FreshBooks from \"@auth/core/providers/freshbooks\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     FreshBooks({\n *       clientId: FRESHBOOKS_CLIENT_ID,\n *       clientSecret: FRESHBOOKS_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [FreshBooks OAuth documentation](https://www.freshbooks.com/api/authenticating-with-oauth-2-0-on-the-new-freshbooks-api\n)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the FreshBooks provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The FreshBooks provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/freshbooks.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Freshbooks(\n  options: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"freshbooks\",\n    name: \"Freshbooks\",\n    type: \"oauth\",\n    authorization: \"https://auth.freshbooks.com/service/auth/oauth/authorize\",\n    token: \"https://api.freshbooks.com/auth/oauth/token\",\n    userinfo: \"https://api.freshbooks.com/auth/api/v1/users/me\",\n    async profile(profile) {\n      return {\n        id: profile.response.id,\n        name: `${profile.response.first_name} ${profile.response.last_name}`,\n        email: profile.response.email,\n        image: null,\n      }\n    },\n    style: {\n      bg: \"#0075dd\",\n      text: \"#fff\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/frontegg.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\"}}>\n * <span style={{fontSize: \"1.35rem\" }}>\n *  Built-in sign in with <b>Frontegg</b> integration.\n * </span>\n * <a href=\"https://frontegg.com\" style={{backgroundColor: \"black\", padding: \"12px\", borderRadius: \"100%\" }}>\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/frontegg.svg\" width=\"24\"/>\n * </a>\n * </div>\n *\n * @module providers/frontegg\n */\n\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\n/** The returned user profile from Frontegg when using the profile callback. [Reference](https://docs.frontegg.com/docs/admin-portal-profile). */\nexport interface FronteggProfile {\n  /** The user's unique Frontegg ID */\n  sub: string\n  /** The user's name */\n  name: string\n  /** The user's email */\n  email: string\n  /** A boolean indicating if the user's email is verified */\n  email_verified: boolean\n  /** The user's picture */\n  profilePictureUrl: string\n  /** The user's roles */\n  roles: string[]\n  /** The user's custom attributes */\n  [claim: string]: unknown\n}\n\n/**\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/frontegg\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Frontegg from \"@auth/core/providers/frontegg\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Frontegg({\n *       clientId: AUTH_FRONTEGG_ID,\n *       clientSecret: AUTH_FRONTEGG_SECRET,\n *       issuer: AUTH_FRONTEGG_ISSUER\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Configuring Frontegg\n *\n * Follow these steps:\n *\n * Log into the [Frontegg portal](https://portal.frontegg.com)\n *\n * Authentication > Login method > Hosted login > Add your callback url here\n *\n * Then, create a `.env.local` file in the project root add the following entries:\n *\n * Get the following from the Frontegg's portal:\n * ```\n * AUTH_FRONTEGG_ID=\"<Client ID>\" # Environments > Your environment > Env settings\n * AUTH_FRONTEGG_SECRET=\"<API KEY>\" # Environments > Your environment > Env settings\n * AUTH_FRONTEGG_ISSUER=\"<https://[YOUR_SUBDOMAIN].frontegg.com>\" # Environments > Your environment > Env settings > Domains > Domain name\n * ```\n *\n * ### Resources\n *\n * - [Frontegg Docs](https://docs.frontegg.com/docs/how-to-use-our-docs)\n *\n * ### Notes\n *\n * The Frontegg provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/frontegg.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::info\n * By default, Auth.js assumes that the Frontegg provider is based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) spec\n * :::\n *\n * ## Help\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n */\nexport default function Frontegg(\n  options: OIDCUserConfig<FronteggProfile>\n): OIDCConfig<FronteggProfile> {\n  return {\n    id: \"frontegg\",\n    name: \"Frontegg\",\n    type: \"oidc\",\n    authorization: `${options.issuer}/oauth/authorize`,\n    token: `${options.issuer}/oauth/token`,\n    userinfo: `${options.issuer}/identity/resources/users/v2/me`,\n    wellKnown: `${options.issuer}/oauth/.well-known/openid-configuration`,\n    issuer: options.issuer,\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/fusionauth.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>FusionAuth</b> integration.</span>\n * <a href=\"https://fusionauth.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/fushionauth.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/fusionauth\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./oauth.js\"\n\n/**\n * This is the default openid signature returned from FusionAuth\n * it can be customized using [lambda functions](https://fusionauth.io/docs/v1/tech/lambdas)\n */\nexport interface FusionAuthProfile extends Record<string, any> {\n  aud: string\n  exp: number\n  iat: number\n  iss: string\n  sub: string\n  jti: string\n  authenticationType: string\n  email: string\n  email_verified: boolean\n  preferred_username?: string\n  name?: string\n  given_name?: string\n  middle_name?: string\n  family_name?: string\n  at_hash: string\n  c_hash: string\n  scope: string\n  sid: string\n  picture?: string\n}\n\n/**\n * Add FusionAuth login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/fusionauth\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import FusionAuth from \"@auth/core/providers/fusionauth\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     FusionAuth({\n *       clientId: FUSIONAUTH_CLIENT_ID,\n *       clientSecret: FUSIONAUTH_CLIENT_SECRET,\n *       tenantId: FUSIONAUTH_TENANT_ID,\n *       issuer: FUSIONAUTH_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n * :::warning\n * If you're using multi-tenancy, you need to pass in the tenantId option to apply the proper theme.\n * :::\n *\n * ### Resources\n *\n *  - [FusionAuth OAuth documentation](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the FusionAuth provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * ## Configuration\n * :::tip\n * An application can be created at https://your-fusionauth-server-url/admin/application.\n *\n * For more information, follow the [FusionAuth 5-minute setup guide](https://fusionauth.io/docs/v1/tech/5-minute-setup-guide).\n * :::\n *\n * In the OAuth settings for your application, configure the following.\n *\n * - Redirect URL\n *   - https://localhost:3000/api/auth/callback/fusionauth\n * - Enabled grants\n *   - Make sure _Authorization Code_ is enabled.\n *\n * If using JSON Web Tokens, you need to make sure the signing algorithm is RS256, you can create an RS256 key pair by\n * going to Settings, Key Master, generate RSA and choosing SHA-256 as algorithm. After that, go to the JWT settings of\n * your application and select this key as Access Token signing key and Id Token signing key.\n * :::tip\n *\n * The FusionAuth provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/fusionauth.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n * \n * \n * It is highly recommended to follow this example call when using the provider in Next.js\n *  so that you can access both the access_token and id_token on the server.\n * \n * ```ts\n * /// <reference types=\"next-auth\" />\nimport NextAuth from 'next-auth';\nexport const { handlers, auth, signIn, signOut } = NextAuth({\n  providers: [\n    {\n      id: 'fusionauth',\n      name: 'FusionAuth',\n      type: 'oidc',\n      issuer: process.env.AUTH_FUSIONAUTH_ISSUER!,\n      clientId: process.env.AUTH_FUSIONAUTH_CLIENT_ID!,\n      clientSecret: process.env.AUTH_FUSIONAUTH_CLIENT_SECRET!,\n      authorization: {\n        params: {\n          scope: 'offline_access email openid profile',\n          tenantId: process.env.AUTH_FUSIONAUTH_TENANT_ID!,\n        },\n      },\n      userinfo: `${process.env.AUTH_FUSIONAUTH_ISSUER}/oauth2/userinfo`,\n      // This is due to a known processing issue\n      // TODO: https://github.com/nextauthjs/next-auth/issues/8745#issuecomment-1907799026\n      token: {\n        url: `${process.env.AUTH_FUSIONAUTH_ISSUER}/oauth2/token`,\n        conform: async (response: Response) => {\n          if (response.status === 401) return response;\n\n          const newHeaders = Array.from(response.headers.entries())\n            .filter(([key]) => key.toLowerCase() !== 'www-authenticate')\n            .reduce(\n              (headers, [key, value]) => (headers.append(key, value), headers),\n              new Headers()\n            );\n\n          return new Response(response.body, {\n            status: response.status,\n            statusText: response.statusText,\n            headers: newHeaders,\n          });\n        },\n      },\n    },\n  ],\n  session: {\n    strategy: 'jwt',\n  },\n  // Required to get the account object in the session and enable\n  // the ability to call API's externally that rely on JWT tokens.\n  callbacks: {\n    async jwt(params) {\n      const { token, user, account } = params;\n      if (account) {\n        // First-time login, save the `access_token`, its expiry and the `refresh_token`\n        return {\n          ...token,\n          ...account,\n        };\n      } else if (\n        token.expires_at &&\n        Date.now() < (token.expires_at as number) * 1000\n      ) {\n        // Subsequent logins, but the `access_token` is still valid\n        return token;\n      } else {\n        // Subsequent logins, but the `access_token` has expired, try to refresh it\n        if (!token.refresh_token) throw new TypeError('Missing refresh_token');\n\n        try {\n          const refreshResponse = await fetch(\n            `${process.env.AUTH_FUSIONAUTH_ISSUER}/oauth2/token`,\n            {\n              method: 'POST',\n              headers: {\n                'Content-Type': 'application/x-www-form-urlencoded',\n              },\n              body: new URLSearchParams({\n                client_id: process.env.AUTH_FUSIONAUTH_CLIENT_ID!,\n                client_secret: process.env.AUTH_FUSIONAUTH_CLIENT_SECRET!,\n                grant_type: 'refresh_token',\n                refresh_token: token.refresh_token as string,\n              }),\n            }\n          );\n\n          if (!refreshResponse.ok) {\n            throw new Error('Failed to refresh token');\n          }\n\n          const tokensOrError = await refreshResponse.json();\n\n          if (!refreshResponse.ok) throw tokensOrError;\n\n          const newTokens = tokensOrError as {\n            access_token: string;\n            expires_in: number;\n            refresh_token?: string;\n          };\n\n          return {\n            ...token,\n            access_token: newTokens.access_token,\n            expires_at: Math.floor(Date.now() / 1000 + newTokens.expires_in),\n            // Some providers only issue refresh tokens once, so preserve if we did not get a new one\n            refresh_token: newTokens.refresh_token\n              ? newTokens.refresh_token\n              : token.refresh_token,\n          };\n        } catch (error) {\n          console.error('Error refreshing access_token', error);\n          // If we fail to refresh the token, return an error so we can handle it on the page\n          token.error = 'RefreshTokenError';\n          return token;\n        }\n      }\n    },\n    async session(params) {\n      const { session, token } = params;\n      return { ...session, ...token };\n    },\n  },\n});\n\ndeclare module 'next-auth' {\n  interface Session {\n    access_token: string;\n    expires_in: number;\n    id_token?: string;\n    expires_at: number;\n    refresh_token?: string;\n    refresh_token_id?: string;\n    error?: 'RefreshTokenError';\n    scope: string;\n    token_type: string;\n    userId: string;\n    provider: string;\n    type: string;\n    providerAccountId: string;\n  }\n}\n\ndeclare module 'next-auth' {\n  interface JWT {\n    access_token: string;\n    expires_in: number;\n    id_token?: string;\n    expires_at: number;\n    refresh_token?: string;\n    refresh_token_id?: string;\n    error?: 'RefreshTokenError';\n    scope: string;\n    token_type: string;\n    userId: string;\n    provider: string;\n    type: string;\n    providerAccountId: string;\n  }\n}\n```\n * \n * \n * \n */\nexport default function FusionAuth<P extends FusionAuthProfile>(\n  // tenantId only needed if there is more than one tenant configured on the server\n  options: OAuthUserConfig<P> & { tenantId?: string }\n): OAuthConfig<P> {\n  return {\n    id: \"fusionauth\",\n    name: \"FusionAuth\",\n    type: \"oidc\",\n    issuer: options.issuer,\n    clientId: options.clientId,\n    clientSecret: options.clientSecret,\n    wellKnown: options?.tenantId\n      ? `${options.issuer}/.well-known/openid-configuration?tenantId=${options.tenantId}`\n      : `${options.issuer}/.well-known/openid-configuration`,\n    authorization: {\n      params: {\n        scope: \"openid offline_access email profile\",\n        ...(options?.tenantId && { tenantId: options.tenantId }),\n      },\n    },\n    userinfo: `${options.issuer}/oauth2/userinfo`,\n    // This is due to a known processing issue\n    // TODO: https://github.com/nextauthjs/next-auth/issues/8745#issuecomment-1907799026\n    token: {\n      url: `${options.issuer}/oauth2/token`,\n      conform: async (response: Response) => {\n        if (response.status === 401) return response\n\n        const newHeaders = Array.from(response.headers.entries())\n          .filter(([key]) => key.toLowerCase() !== \"www-authenticate\")\n          .reduce(\n            (headers, [key, value]) => (headers.append(key, value), headers),\n            new Headers()\n          )\n\n        return new Response(response.body, {\n          status: response.status,\n          statusText: response.statusText,\n          headers: newHeaders,\n        })\n      },\n    },\n    checks: [\"pkce\", \"state\"],\n    profile(profile) {\n      return {\n        id: profile.sub,\n        email: profile.email,\n        name:\n          profile.name ??\n          profile.preferred_username ??\n          [profile.given_name, profile.middle_name, profile.family_name]\n            .filter((x) => x)\n            .join(\" \"),\n        image: profile.picture,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/github.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#24292f\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>GitHub</b> integration.</span>\n * <a href=\"https://github.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/github.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/github\n */\n\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface GitHubEmail {\n  email: string\n  primary: boolean\n  verified: boolean\n  visibility: \"public\" | \"private\"\n}\n\n/** @see [Get the authenticated user](https://docs.github.com/en/rest/users/users#get-the-authenticated-user) */\nexport interface GitHubProfile {\n  login: string\n  id: number\n  node_id: string\n  avatar_url: string\n  gravatar_id: string | null\n  url: string\n  html_url: string\n  followers_url: string\n  following_url: string\n  gists_url: string\n  starred_url: string\n  subscriptions_url: string\n  organizations_url: string\n  repos_url: string\n  events_url: string\n  received_events_url: string\n  type: string\n  site_admin: boolean\n  name: string | null\n  company: string | null\n  blog: string | null\n  location: string | null\n  email: string | null\n  hireable: boolean | null\n  bio: string | null\n  twitter_username?: string | null\n  public_repos: number\n  public_gists: number\n  followers: number\n  following: number\n  created_at: string\n  updated_at: string\n  private_gists?: number\n  total_private_repos?: number\n  owned_private_repos?: number\n  disk_usage?: number\n  suspended_at?: string | null\n  collaborators?: number\n  two_factor_authentication: boolean\n  plan?: {\n    collaborators: number\n    name: string\n    space: number\n    private_repos: number\n  }\n  [claim: string]: unknown\n}\n\n/**\n * Add GitHub login to your page and make requests to [GitHub APIs](https://docs.github.com/en/rest).\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/github\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import GitHub from \"@auth/core/providers/github\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     GitHub({ clientId: GITHUB_CLIENT_ID, clientSecret: GITHUB_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [GitHub - Creating an OAuth App](https://docs.github.com/en/developers/apps/building-oauth-apps/creating-an-oauth-app)\n * - [GitHub - Authorizing OAuth Apps](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps)\n * - [GitHub - Configure your GitHub OAuth Apps](https://github.com/settings/developers)\n * - [Learn more about OAuth](https://authjs.dev/concepts/oauth)\n * - [Source code](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/github.ts)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the GitHub provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The GitHub provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/github.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function GitHub(\n  config: OAuthUserConfig<GitHubProfile> & {\n    /** Configuration for usage with [GitHub Enterprise Server](https://docs.github.com/en/enterprise-server/get-started). */\n    enterprise?: {\n      /** The base URL of your GitHub Enterprise Server instance. */\n      baseUrl?: string\n    }\n  }\n): OAuthConfig<GitHubProfile> {\n  const baseUrl = config?.enterprise?.baseUrl ?? \"https://github.com\"\n  const apiBaseUrl = config?.enterprise?.baseUrl\n    ? `${config?.enterprise?.baseUrl}/api/v3`\n    : \"https://api.github.com\"\n\n  return {\n    id: \"github\",\n    name: \"GitHub\",\n    type: \"oauth\",\n    authorization: {\n      url: `${baseUrl}/login/oauth/authorize`,\n      params: { scope: \"read:user user:email\" },\n    },\n    token: `${baseUrl}/login/oauth/access_token`,\n    userinfo: {\n      url: `${apiBaseUrl}/user`,\n      async request({ tokens, provider }) {\n        const profile = await fetch(provider.userinfo?.url as URL, {\n          headers: {\n            Authorization: `Bearer ${tokens.access_token}`,\n            \"User-Agent\": \"authjs\",\n          },\n        }).then(async (res) => await res.json())\n\n        if (!profile.email) {\n          // If the user does not have a public email, get another via the GitHub API\n          // See https://docs.github.com/en/rest/users/emails#list-public-email-addresses-for-the-authenticated-user\n          const res = await fetch(`${apiBaseUrl}/user/emails`, {\n            headers: {\n              Authorization: `Bearer ${tokens.access_token}`,\n              \"User-Agent\": \"authjs\",\n            },\n          })\n\n          if (res.ok) {\n            const emails: GitHubEmail[] = await res.json()\n            profile.email = (emails.find((e) => e.primary) ?? emails[0]).email\n          }\n        }\n\n        return profile\n      },\n    },\n    profile(profile) {\n      return {\n        id: profile.id.toString(),\n        name: profile.name ?? profile.login,\n        email: profile.email,\n        image: profile.avatar_url,\n      }\n    },\n    style: { bg: \"#24292f\", text: \"#fff\" },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/gitlab.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>GitLab</b> integration.</span>\n * <a href=\"https://gitlab.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/gitlab.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/gitlab\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface GitLabProfile extends Record<string, any> {\n  id: number\n  username: string\n  email: string\n  name: string\n  state: string\n  avatar_url: string\n  web_url: string\n  created_at: string\n  bio: string\n  location?: string\n  public_email: string\n  skype: string\n  linkedin: string\n  twitter: string\n  website_url: string\n  organization: string\n  job_title: string\n  pronouns: string\n  bot: boolean\n  work_information?: string\n  followers: number\n  following: number\n  local_time: string\n  last_sign_in_at: string\n  confirmed_at: string\n  theme_id: number\n  last_activity_on: string\n  color_scheme_id: number\n  projects_limit: number\n  current_sign_in_at: string\n  identities: Array<{\n    provider: string\n    extern_uid: string\n  }>\n  can_create_group: boolean\n  can_create_project: boolean\n  two_factor_enabled: boolean\n  external: boolean\n  private_profile: boolean\n  commit_email: string\n  shared_runners_minutes_limit: number\n  extra_shared_runners_minutes_limit: number\n}\n\n/**\n * Add GitLab login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/gitlab\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import GitLab from \"@auth/core/providers/gitlab\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     GitLab({ clientId: GITLAB_CLIENT_ID, clientSecret: GITLAB_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [GitLab OAuth documentation](https://docs.gitlab.com/ee/api/oauth2.html)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the GitLab provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n * Enable the `read_user` option in scope if you want to save the users email address on sign up.\n * :::\n *\n * :::tip\n *\n * The GitLab provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/gitlab.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function GitLab<P extends GitLabProfile>(\n  options: OAuthUserConfig<P> & {\n    /**\n     * @default \"https://gitlab.com\"\n     */\n    baseUrl?: URL | string\n  }\n): OAuthConfig<P> {\n  const baseUrl = options.baseUrl ?? \"https://gitlab.com\"\n  const url = new URL(baseUrl.toString())\n\n  return {\n    id: \"gitlab\",\n    name: \"GitLab\",\n    type: \"oauth\",\n    authorization: `${url}oauth/authorize?scope=read_user`,\n    token: `${url}oauth/token`,\n    userinfo: `${url}api/v4/user`,\n    profile(profile) {\n      return {\n        id: profile.sub?.toString() ?? profile.id?.toString(),\n        name: profile.name ?? profile.username,\n        email: profile.email,\n        image: profile.avatar_url,\n      }\n    },\n    style: { bg: \"#FC6D26\", text: \"#fff\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/google.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Google</b> integration.</span>\n * <a href=\"https://google.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/google.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/google\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface GoogleProfile extends Record<string, any> {\n  aud: string\n  azp: string\n  email: string\n  email_verified: boolean\n  exp: number\n  family_name?: string\n  given_name: string\n  hd?: string\n  iat: number\n  iss: string\n  jti?: string\n  locale?: string\n  name: string\n  nbf?: number\n  picture: string\n  sub: string\n}\n\n/**\n * Add Google login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/google\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Google from \"@auth/core/providers/google\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Google({ clientId: GOOGLE_CLIENT_ID, clientSecret: GOOGLE_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Google OAuth documentation](https://developers.google.com/identity/protocols/oauth2)\n *  - [Google OAuth Configuration](https://console.developers.google.com/apis/credentials)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Google provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n *\n * The \"Authorized redirect URIs\" used when creating the credentials must include your full domain and end in the callback path. For example;\n *\n * - For production: `https://{YOUR_DOMAIN}/api/auth/callback/google`\n * - For development: `http://localhost:3000/api/auth/callback/google`\n *\n * :::warning\n * Google only provides Refresh Token to an application the first time a user signs in.\n *\n * To force Google to re-issue a Refresh Token, the user needs to remove the application from their account and sign in again:\n * https://myaccount.google.com/permissions\n *\n * Alternatively, you can also pass options in the `params` object of `authorization` which will force the Refresh Token to always be provided on sign in, however this will ask all users to confirm if they wish to grant your application access every time they sign in.\n *\n * If you need access to the RefreshToken or AccessToken for a Google account and you are not using a database to persist user accounts, this may be something you need to do.\n *\n * ```ts\n * const options = {\n *   providers: [\n *     Google({\n *       clientId: process.env.GOOGLE_ID,\n *       clientSecret: process.env.GOOGLE_SECRET,\n *       authorization: {\n *         params: {\n *           prompt: \"consent\",\n *           access_type: \"offline\",\n *           response_type: \"code\"\n *         }\n *       }\n *     })\n *   ],\n * }\n * ```\n *\n * :::\n *\n * :::tip\n * Google also returns a `email_verified` boolean property in the OAuth profile.\n *\n * You can use this property to restrict access to people with verified accounts at a particular domain.\n *\n * ```ts\n * const options = {\n *   ...\n *   callbacks: {\n *     async signIn({ account, profile }) {\n *       if (account.provider === \"google\") {\n *         return profile.email_verified && profile.email.endsWith(\"@example.com\")\n *       }\n *       return true // Do different verification for other providers that don't have `email_verified`\n *     },\n *   }\n *   ...\n * }\n * ```\n *\n * :::\n * :::tip\n *\n * The Google provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/google.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Google<P extends GoogleProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"google\",\n    name: \"Google\",\n    type: \"oidc\",\n    issuer: \"https://accounts.google.com\",\n    style: {\n      brandColor: \"#1a73e8\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/hubspot.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>HubSpot</b> integration.</span>\n * <a href=\"https://hubspot.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/hubspot.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/hubspot\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\ninterface HubSpotProfile extends Record<string, any> {\n  // https://legacydocs.hubspot.com/docs/methods/oauth2/get-access-token-information\n  user: string\n  user_id: string\n  hub_domain: string\n  hub_id: string\n}\n\n/**\n * Add HubSpot login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/hubspot\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import HubSpot from \"@auth/core/providers/hubspot\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     HubSpot({\n *       clientId: HUBSPOT_CLIENT_ID,\n *       clientSecret: HUBSPOT_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [HubSpot OAuth documentation](https://developers.hubspot.com/docs/api/oauth-quickstart-guide)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the HubSpot provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * You need to have an APP in your Developer Account as described at https://developers.hubspot.com/docs/api/developer-tools-overview\n * :::note\n * HubSpot returns a limited amount of information on the token holder (see [docs](https://legacydocs.hubspot.com/docs/methods/oauth2/get-access-token-information)). One other issue is that the name and profile photo cannot be fetched through API as discussed [here](https://community.hubspot.com/t5/APIs-Integrations/Profile-photo-is-not-retrieved-with-User-API/m-p/325521).\n * :::\n * :::tip\n *\n * The HubSpot provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/hubspot.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function HubSpot<P extends HubSpotProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"hubspot\",\n    name: \"HubSpot\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://app.hubspot.com/oauth/authorize\",\n      params: { scope: \"oauth\", client_id: options.clientId },\n    },\n    client: {\n      token_endpoint_auth_method: \"client_secret_post\",\n    },\n    token: \"https://api.hubapi.com/oauth/v1/token\",\n    userinfo: {\n      url: \"https://api.hubapi.com/oauth/v1/access-tokens\",\n      async request({ tokens, provider }) {\n        const url = `${provider.userinfo?.url}/${tokens.access_token}`\n\n        return await fetch(url, {\n          headers: { \"Content-Type\": \"application/json\" },\n          method: \"GET\",\n        }).then(async (res) => await res.json())\n      },\n    },\n    profile(profile) {\n      return {\n        id: profile.user_id,\n        name: profile.user,\n        email: profile.user,\n\n        // TODO: get image from profile once it's available\n        // Details available https://community.hubspot.com/t5/APIs-Integrations/Profile-photo-is-not-retrieved-with-User-API/m-p/325521\n        image: null,\n      }\n    },\n    style: { bg: \"#ff7a59\", text: \"#fff\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/huggingface.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#fff\", display: \"flex\", justifyContent: \"space-between\", color: \"#000\", padding: 16}}>\n * <span>Built-in <b>Hugging Face</b> integration.</span>\n * <a href=\"https://huggingface.co\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/huggingface.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/huggingface\n */\n\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\nexport interface HuggingfaceProfile {\n  /**\n   * Unique identifier for the user.\n   */\n  sub: string\n  /**\n   * Full name of the user.\n   *\n   * Needs 'profile' scope\n   */\n  name?: string\n  /**\n   * Username of the user.\n   *\n   * Need 'profile' scope\n   */\n  preferred_username?: string\n  /**\n   * URL of the user's avatar.\n   *\n   * Need 'profile' scope\n   */\n  profile?: string\n  /**\n   * URL of the user's profile picture.\n   *\n   * Need 'profile' scope\n   */\n  picture?: string\n  /**\n   * Need 'profile' scope\n   *\n   * Website of the user.\n   */\n  website?: string\n  /**\n   * Need 'email' scope\n   *\n   * Email address of the user.\n   */\n  email?: string\n  /**\n   * Need 'email' scope\n   *\n   * Whether the user's email address is verified. Should always be true, Hugging Face enforces\n   * email verification for users to grant access to OAuth apps.\n   */\n  email_verified?: boolean\n  /**\n   * Whether the user has a paid subscription.\n   */\n  isPro: boolean\n  /**\n   * Whether the user has a payment method set up.\n   *\n   * Needs the `read-billing` scope.\n   */\n  canPay?: boolean\n  /**\n   * List of the user's organizations.\n   */\n  orgs: Array<{\n    /**\n     * Unique identifier for the organization.\n     */\n    sub: string\n    /**\n     * Name of the organization.\n     */\n    name: string\n    /**\n     * URL of the organization's avatar.\n     */\n    picture: string\n    /**\n     * Username of the organization.\n     */\n    preferred_username: string\n    /**\n     * Whether the organization has a paid enterprise subscription.\n     */\n    isEnterprise: boolean\n    /**\n     * Whether the organization has a payment method set up.\n     *\n     * Access to the organization needs to be granted to the oauth app for this field to be present.\n     */\n    canPay?: boolean\n    /**\n     * The role of the user in the organization.\n     *\n     * Access to the organization needs to be granted to the oauth app for this field to be present.\n     */\n    roleInOrg?: \"admin\" | \"write\" | \"read\" | \"contributor\"\n    /**\n     * User needs to re-authenticate to access the organization.\n     *\n     * Access to the organization needs to be granted to the oauth app for this field to be present.\n     */\n    pendingSSO?: boolean\n    /**\n     * User needs to enable MFA to access the organization.\n     *\n     * Access to the organization needs to be granted to the oauth app for this field to be present.\n     */\n    missingMFA?: boolean\n    /**\n     * Resource groups are a feature of enterprise organizations.\n     *\n     * They allow granular access control to resources within the organization.\n     *\n     * Access to the organization needs to be granted to the oauth app for this field to be present.\n     */\n    resourceGroups?: Array<{\n      /**\n       * Unique identifier for the resource group.\n       */\n      sub: string\n      name: string\n      /**\n       * The role of the user in the resource group.\n       */\n      role: \"read\" | \"write\" | \"admin\" | \"contributor\"\n    }>\n  }>\n}\n\n/**\n * Add HuggingFace login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/huggingface\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import HuggingFace from \"@auth/core/providers/huggingface\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     HuggingFace({\n *       clientId: HUGGINGFACE_CLIENT_ID,\n *       clientSecret: HUGGINGFACE_CLIENT_SECRET,\n *       authorization: {\n *        params: {\n *          scope: \"openid profile email\", // specify the scopes you need\n *          //  orgIds: \"unique_org_id\" // If your oauth app needs access to a specific organization of the user\n *        }\n *       },\n *     }),\n *   ],\n * })\n * ```\n *\n * The following scopes are available:\n *\n * - `openid`: Grants access to the user's OpenID Connect profile.\n * - `profile`: Grants access to the user's profile information.\n * - `email`: Grants access to the user's email address.\n * - `read-repos`: Grants read access to the user's repositories.\n * - `write-repos`: Grants write access to the user's repositories.\n * - `manage-repos`: Can create/delete repositories on behalf of the user.\n * - `write-discussions`: Can post on the user's behalf.\n * - `read-billing`: Know if the user has a payment method set up.\n * - `inference-api`: Can make calls to Inference providers on behalf of the user.\n * - `webhooks`: Can manage webhooks on behalf of the user.\n *\n * You need to enable them first in your OAuth app settings.\n *\n * /!\\ By default, the `profile` and `email` scopes are enabled in NextAuth. So you need to enable\n * the `email` scope in your OAuth app settings or you will get a scope error.\n *\n * ### Resources\n *\n *  - [Hugging Face OAuth documentation](https://huggingface.co/docs/hub/en/oauth#creating-an-oauth-app)\n *  - [Create an OAuth application](https://huggingface.co/settings/applications/new)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Hugging Face provider is\n * based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * The HuggingFace provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/huggingface.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Huggingface(\n  options: OIDCUserConfig<HuggingfaceProfile>\n): OIDCConfig<HuggingfaceProfile> {\n  return {\n    id: \"huggingface\",\n    name: \"Hugging Face\",\n    type: \"oidc\",\n    issuer: \"https://huggingface.co\",\n    checks: [\"state\", \"pkce\"],\n\n    style: {\n      bg: \"#FFD21E\",\n      text: \"#000\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/identity-server4.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>IdentityServer4</b> integration.</span>\n * <a href=\"https://identityserver4.readthedocs.io\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/identity-server4.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/identity-server4\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add IdentityServer4 login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/identity-server4\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import IdentityServer4 from \"@auth/core/providers/identity-server4\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     IdentityServer4({\n *       clientId: IDENTITY_SERVER4_CLIENT_ID,\n *       clientSecret: IDENTITY_SERVER4_CLIENT_SECRET,\n *       issuer: IDENTITY_SERVER4_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [IdentityServer4 OAuth documentation](https://identityserver4.readthedocs.io/en/latest/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the IdentityServer4 provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::warning\n * IdentityServer4 is discontinued and only releases security updates until November 2022. You should consider an alternative provider.\n * :::\n * :::tip\n *\n * The IdentityServer4 provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/identity-server4.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function IdentityServer4(\n  options: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"identity-server4\",\n    name: \"IdentityServer4\",\n    type: \"oidc\",\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/index.ts",
    "content": "import type { Profile } from \"../types.js\"\nimport CredentialsProvider from \"./credentials.js\"\nimport type { CredentialsConfig, CredentialsProviderId } from \"./credentials.js\"\nimport type EmailProvider from \"./email.js\"\nimport type { EmailConfig, EmailProviderId } from \"./email.js\"\nimport type {\n  OAuth2Config,\n  OAuthConfig,\n  OAuthProviderId,\n  OIDCConfig,\n} from \"./oauth.js\"\nimport type { WebAuthnConfig, WebAuthnProviderType } from \"./webauthn.js\"\n\nexport * from \"./credentials.js\"\nexport * from \"./email.js\"\nexport * from \"./oauth.js\"\n\n/**\n * Providers passed to Auth.js must define one of these types.\n *\n * @see [RFC 6749 - The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html#section-2.3)\n * @see [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)\n * @see [Email or Passwordless Authentication](https://authjs.dev/concepts/oauth)\n * @see [Credentials-based Authentication](https://authjs.dev/getting-started/providers/credentials)\n */\nexport type ProviderType =\n  | \"oidc\"\n  | \"oauth\"\n  | \"email\"\n  | \"credentials\"\n  | WebAuthnProviderType\n\n/** Shared across all {@link ProviderType} */\nexport interface CommonProviderOptions {\n  /**\n   * Uniquely identifies the provider in {@link AuthConfig.providers}\n   * It's also part of the URL\n   */\n  id: string\n  /**\n   * The provider name used on the default sign-in page's sign-in button.\n   * For example if it's \"Google\", the corresponding button will say:\n   * \"Sign in with Google\"\n   */\n  name: string\n  /** See {@link ProviderType} */\n  type: ProviderType\n}\n\ninterface InternalProviderOptions {\n  /** Used to deep merge user-provided config with the default config\n   */\n  options?: Record<string, unknown>\n}\n\n/**\n * Must be a supported authentication provider config:\n * - {@link OAuthConfig}\n * - {@link EmailConfigInternal}\n * - {@link CredentialsConfigInternal}\n *\n * For more information, see the guides:\n *\n * @see [OAuth/OIDC guide](https://authjs.dev/guides/providers/custom-provider)\n * @see [Email (Passwordless) guide](https://authjs.dev/guides/providers/email)\n * @see [Credentials guide](https://authjs.dev/getting-started/providers/credentials)\n */\nexport type Provider<P extends Profile = any> = (\n  | ((\n      | OIDCConfig<P>\n      | OAuth2Config<P>\n      | EmailConfig\n      | CredentialsConfig\n      | WebAuthnConfig\n    ) &\n      InternalProviderOptions)\n  | ((\n      ...args: any\n    ) => (\n      | OAuth2Config<P>\n      | OIDCConfig<P>\n      | EmailConfig\n      | CredentialsConfig\n      | WebAuthnConfig\n    ) &\n      InternalProviderOptions)\n) &\n  InternalProviderOptions\n\nexport type BuiltInProviders = Record<\n  OAuthProviderId,\n  (config: Partial<OAuthConfig<any>>) => OAuthConfig<any>\n> &\n  Record<CredentialsProviderId, typeof CredentialsProvider> &\n  Record<EmailProviderId, typeof EmailProvider> &\n  Record<\n    WebAuthnProviderType,\n    (config: Partial<WebAuthnConfig>) => WebAuthnConfig\n  >\n\nexport type AppProviders = Array<\n  Provider | ReturnType<BuiltInProviders[keyof BuiltInProviders]>\n>\n\nexport interface AppProvider extends CommonProviderOptions {\n  signinUrl: string\n  callbackUrl: string\n}\n\nexport type ProviderId =\n  | CredentialsProviderId\n  | EmailProviderId\n  | OAuthProviderId\n  | WebAuthnProviderType\n  | (string & {}) // HACK: To allow user-defined providers in `signIn`\n"
  },
  {
    "path": "packages/core/src/providers/instagram.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Instagram</b> integration.</span>\n * <a href=\"https://www.instagram.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/instagram.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/instagram\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add Instagram login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/instagram\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Instagram from \"@auth/core/providers/instagram\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Instagram({\n *       clientId: INSTAGRAM_CLIENT_ID,\n *       clientSecret: INSTAGRAM_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Instagram OAuth documentation](https://developers.facebook.com/docs/instagram-basic-display-api/getting-started)\n *  - [Instagram OAuth apps](https://developers.facebook.com/apps/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Instagram provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n *\n * :::warning\n * Email address is not returned by the Instagram API.\n * :::\n *\n * :::tip\n * Instagram display app required callback URL to be configured in your Facebook app and Facebook required you to use **https** even for localhost! In order to do that, you either need to [add an SSL to your localhost](https://www.freecodecamp.org/news/how-to-get-https-working-on-your-local-development-environment-in-5-minutes-7af615770eec/) or use a proxy such as [ngrok](https://ngrok.com/docs).\n * :::\n * :::tip\n *\n * The Instagram provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/instagram.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Instagram(\n  config: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"instagram\",\n    name: \"Instagram\",\n    type: \"oauth\",\n    authorization:\n      \"https://api.instagram.com/oauth/authorize?scope=user_profile\",\n    token: \"https://api.instagram.com/oauth/access_token\",\n    userinfo:\n      \"https://graph.instagram.com/me?fields=id,username,account_type,name\",\n    client: {\n      token_endpoint_auth_method: \"client_secret_post\",\n    },\n    async profile(profile) {\n      return {\n        id: profile.id,\n        name: profile.username,\n        email: null,\n        image: null,\n      }\n    },\n    style: {\n      bg: \"#fff\",\n      text: \"#000\",\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/kakao.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Kakao</b> integration.</span>\n * <a href=\"https://www.kakaocorp.com/page/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/kakao.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/kakao\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport type DateTime = string\nexport type Gender = \"female\" | \"male\"\nexport type Birthday = \"SOLAR\" | \"LUNAR\"\nexport type AgeRange =\n  | \"1-9\"\n  | \"10-14\"\n  | \"15-19\"\n  | \"20-29\"\n  | \"30-39\"\n  | \"40-49\"\n  | \"50-59\"\n  | \"60-69\"\n  | \"70-79\"\n  | \"80-89\"\n  | \"90-\"\n\n/**\n * https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#req-user-info\n * type from : https://gist.github.com/ziponia/cdce1ebd88f979b2a6f3f53416b56a77\n */\nexport interface KakaoProfile extends Record<string, any> {\n  id: number\n  has_signed_up?: boolean\n  connected_at?: DateTime\n  synched_at?: DateTime\n  properties?: {\n    id?: string\n    status?: string\n    registered_at?: DateTime\n    msg_blocked?: boolean\n    nickname?: string\n    profile_image?: string\n    thumbnail_image?: string\n  }\n  kakao_account?: {\n    profile_needs_agreement?: boolean\n    profile_nickname_needs_agreement?: boolean\n    profile_image_needs_agreement?: boolean\n    profile?: {\n      nickname?: string\n      thumbnail_image_url?: string\n      profile_image_url?: string\n      is_default_image?: boolean\n    }\n    name_needs_agreement?: boolean\n    name?: string\n    email_needs_agreement?: boolean\n    is_email_valid?: boolean\n    is_email_verified?: boolean\n    email?: string\n    age_range_needs_agreement?: boolean\n    age_range?: AgeRange\n    birthyear_needs_agreement?: boolean\n    birthyear?: string\n    birthday_needs_agreement?: boolean\n    birthday?: string\n    birthday_type?: Birthday\n    gender_needs_agreement?: boolean\n    gender?: Gender\n    phone_number_needs_agreement?: boolean\n    phone_number?: string\n    ci_needs_agreement?: boolean\n    ci?: string\n    ci_authenticated_at?: DateTime\n  }\n}\n\n/**\n * Add Kakao login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/kakao\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Kakao from \"@auth/core/providers/kakao\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Kakao({ clientId: KAKAO_CLIENT_ID, clientSecret: KAKAO_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Kakao OAuth documentation](https://developers.kakao.com/product/kakaoLogin)\n *  - [Kakao OAuth configuration](https://developers.kakao.com/docs/latest/en/kakaologin/common)\n *\n * ## Configuration\n * Create a provider and a Kakao application at https://developers.kakao.com/console/app. In the settings of the app under Kakao Login, activate web app, change consent items and configure callback URL.\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Kakao provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n *\n * The \"Authorized redirect URIs\" used when creating the credentials must include your full domain and end in the callback path. For example;\n *\n * ![스크린샷 2023-11-28 오후 9 27 41](https://github.com/nextauthjs/next-auth/assets/66895208/7d4c2df6-45a6-4937-bb10-4b47c987bff4)\n *\n * - For production: `https://{YOUR_DOMAIN}/api/auth/callback/kakao`\n * - For development: `http://localhost:3000/api/auth/callback/kakao`\n *\n * :::tip\n *\n * The Kakao provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/kakao.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::tip\n *\n * Kakao's client key is in **Summary(It is written as 요약정보 in Korean.) tab's App Keys Field**\n * (My Application > App Settings > Summary)\n *\n * ![스크린샷 2023-11-28 오후 9 47 17](https://github.com/nextauthjs/next-auth/assets/66895208/a87e5705-26b9-4f83-99d7-6df097a3632c)\n *\n * Kakao's clientSecret key is in **Security(It is written as 보안 in Korean.) tab's App Keys Field**\n * (My Application > Product Settings > Kakao Login > Security)\n *\n * ![스크린샷 2023-11-28 오후 9 38 25](https://github.com/nextauthjs/next-auth/assets/66895208/6a763921-4f70-40f4-a3e1-9abc78276d45)\n *\n * :::\n *\n * :::tip\n *\n * Kakao dev console has a button at the top right to change from KR to ENG\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Kakao<P extends KakaoProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"kakao\",\n    name: \"Kakao\",\n    type: \"oauth\",\n    authorization: \"https://kauth.kakao.com/oauth/authorize?scope\",\n    token: \"https://kauth.kakao.com/oauth/token\",\n    userinfo: \"https://kapi.kakao.com/v2/user/me\",\n    client: {\n      token_endpoint_auth_method: \"client_secret_post\",\n    },\n    profile(profile) {\n      return {\n        id: profile.id.toString(),\n        name: profile.kakao_account?.profile?.nickname,\n        email: profile.kakao_account?.email,\n        image: profile.kakao_account?.profile?.profile_image_url,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/keycloak.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Keycloak</b> integration.</span>\n * <a href=\"https://keycloak.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/keycloak.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/keycloak\n */\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\nexport interface KeycloakProfile extends Record<string, any> {\n  exp: number\n  iat: number\n  auth_time: number\n  jti: string\n  iss: string\n  aud: string\n  sub: string\n  typ: string\n  azp: string\n  session_state: string\n  at_hash: string\n  acr: string\n  sid: string\n  email_verified: boolean\n  name: string\n  preferred_username: string\n  given_name: string\n  family_name: string\n  email: string\n  picture: string\n  user: any\n}\n\n/**\n * Add Keycloak login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/keycloak\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Keycloak from \"@auth/core/providers/keycloak\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Keycloak({\n *       clientId: KEYCLOAK_CLIENT_ID,\n *       clientSecret: KEYCLOAK_CLIENT_SECRET,\n *       issuer: KEYCLOAK_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Keycloak OIDC documentation](https://www.keycloak.org/docs/latest/server_admin/#_oidc_clients)\n *\n * :::tip\n *\n * Create an openid-connect client in Keycloak with \"confidential\" as the \"Access Type\".\n *\n * :::\n *\n * :::note\n *\n * issuer should include the realm – e.g. https://my-keycloak-domain.com/realms/My_Realm\n *\n * :::\n * ### Notes\n *\n * By default, Auth.js assumes that the Keycloak provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * The Keycloak provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/keycloak.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Keycloak<P extends KeycloakProfile>(\n  options: OIDCUserConfig<P>\n): OIDCConfig<P> {\n  return {\n    id: \"keycloak\",\n    name: \"Keycloak\",\n    type: \"oidc\",\n    style: { brandColor: \"#428bca\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/kinde.ts",
    "content": "/**\n * <div class=\"provider\" style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\"}}>\n * <span style={{fontSize: \"1.35rem\" }}>\n *  Built-in sign in with <b>Kinde</b> integration.\n * </span>\n * <a href=\"https://kinde.com\" style={{backgroundColor: \"#0F0F0F\", padding: \"12px\", borderRadius: \"100%\" }}>\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/kinde.svg\" width=\"24\"/>\n * </a>\n * </div>\n *\n * @module providers/kinde\n */\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\n/** The returned user profile from Kinde when using the profile callback. [Reference](https://kinde.com/api/docs/#get-user-profile). */\nexport interface KindeProfile extends Record<string, any> {\n  /** The user's given name. */\n  first_name: string\n  /** The user's unique identifier. */\n  id: string\n  /** The user's family name. */\n  last_name: string\n  /** URL pointing to the user's profile picture. */\n  picture: string\n  /** The user's email address. */\n  preferred_email: string\n  /** The user's identifier from a previous system. */\n  provided_id: string\n  /** The user's username. */\n  username: string\n}\n\n/**\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/kinde\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Kinde from \"@auth/core/providers/kinde\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Kinde({\n *       clientId: KINDE_CLIENT_ID,\n *       clientSecret: KINDE_CLIENT_SECRET,\n *       issuer: KINDE_DOMAIN,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Kinde docs](https://docs.kinde.com/)\n *\n * ### Notes\n *\n * The Kinde provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/kinde.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * ## Help\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n */\nexport default function Kinde(\n  config: OIDCUserConfig<KindeProfile>\n): OIDCConfig<KindeProfile> {\n  return {\n    id: \"kinde\",\n    name: \"Kinde\",\n    type: \"oidc\",\n    style: { text: \"#0F0F0F\", bg: \"#fff\" },\n    options: config,\n    checks: [\"state\", \"pkce\"],\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/line.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>LINE</b> integration.</span>\n * <a href=\"https://LINE.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/line.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/line\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface LineProfile extends Record<string, any> {\n  iss: string\n  sub: string\n  aud: string\n  exp: number\n  iat: number\n  amr: string[]\n  name: string\n  picture: string\n  user: any\n}\n\n/**\n * Add LINE login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/line\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import LINE from \"@auth/core/providers/line\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     LINE({ clientId: LINE_CLIENT_ID, clientSecret: LINE_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [LINE Login documentation](https://developers.line.biz/en/docs/line-login/integrate-line-login/)\n *  - [LINE app console](https://developers.line.biz/console/)\n *\n * ## Configuration\n * Create a provider and a LINE login channel at https://developers.line.biz/console/. In the settings of the channel under LINE Login, activate web app and configure the following: Callback URL `http://localhost:3000/api/auth/callback/line`\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the LINE provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * To retrieve email address, you need to apply for Email address permission. Open [Line Developer Console](https://developers.line.biz/console/), go to your Login Channel. Scroll down bottom to find **OpenID Connect** -> **Email address permission**. Click **Apply** and follow the instruction.\n *\n * :::\n *\n * :::tip\n *\n * The LINE provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/line.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function LINE<P extends LineProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"line\",\n    name: \"LINE\",\n    type: \"oidc\",\n    issuer: \"https://access.line.me\",\n    client: {\n      id_token_signed_response_alg: \"HS256\",\n    },\n    style: { bg: \"#00C300\", text: \"#fff\" },\n    options,\n    checks: [\"state\"],\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/linkedin.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>LinkedIn</b> integration.</span>\n * <a href=\"https://linkedin.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/linkedin.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/linkedin\n */\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\n/** @see https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2#response-body-schema */\nexport interface LinkedInProfile extends Record<string, any> {\n  sub: string\n  name: string\n  given_name: string\n  family_name: string\n  picture: string\n  locale: string\n  email: string\n  email_verified: boolean\n}\n\n/**\n * Add LinkedIn login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/linkedin\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import LinkedIn from \"@auth/core/providers/linkedin\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     LinkedIn({\n *       clientId: LINKEDIN_CLIENT_ID,\n *       clientSecret: LINKEDIN_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [LinkedIn OAuth documentation](https://docs.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow)\n *  - [LinkedIn app console](https://www.linkedin.com/developers/apps/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the LinkedIn provider is\n * based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * The LinkedIn provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/linkedin.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function LinkedIn<P extends LinkedInProfile>(\n  options: OIDCUserConfig<P>\n): OIDCConfig<P> {\n  return {\n    id: \"linkedin\",\n    name: \"LinkedIn\",\n    type: \"oidc\",\n    client: { token_endpoint_auth_method: \"client_secret_post\" },\n    issuer: \"https://www.linkedin.com/oauth\",\n    style: { bg: \"#069\", text: \"#fff\" },\n    checks: [\"state\"],\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/logto.ts",
    "content": "/**\n * <div style={{display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\"}}>\n * <span style={{fontSize: \"1.35rem\" }}>\n *  Built-in sign in with <b>Logto</b> integration.\n * </span>\n * <a href=\"https://logto.io\" style={{backgroundColor: \"black\", padding: \"12px\", borderRadius: \"100%\" }}>\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/logto.svg\" width=\"24\"/>\n * </a>\n * </div>\n *\n * @module providers/logto\n */\n\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\n/** The returned user profile from Logto when using the profile callback. [Reference](https://docs.logto.io/quick-starts/next-auth#scopes-and-claims). */\nexport interface LogtoProfile {\n  /** The user's unique ID */\n  sub: string\n  /** The user's name */\n  name: string\n  /** The user's username */\n  username: string\n  /** The user's picture */\n  picture: string\n  /** The user's email */\n  email: string\n  /** A boolean indicating if the user's email is verified */\n  email_verified: boolean\n  /** The user's phone number */\n  phone_number: string\n  /** A boolean indicating if the user's phone number is verified */\n  phone_number_verified: boolean\n  /** The user's address */\n  address: string\n  /** Custom fields */\n  custom_data: object\n  /** The linked identities of the user */\n  identities: object\n  /** The linked SSO identities of the user */\n  sso_identities: object[]\n  /** The organization IDs the user belongs to */\n  organizations: string[]\n  /** The organization data the user belongs to */\n  organization_data: object[]\n  /** The organization roles the user belongs to with the format of organization_id:/role_name */\n  organization_roles: string[]\n  /** The user's custom attributes */\n  [claim: string]: unknown\n}\n\n/**\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/logto\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Logto from \"@auth/core/providers/logto\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Logto({\n *       clientId: LOGTO_ID,\n *       clientSecret: LOGTO_SECRET,\n *       issuer: LOGTO_ISSUER\n *     }),\n *   ],\n * })\n * ```\n *\n *\n * ### Resources\n *\n * - [Logto Auth.js quickstart](https://docs.logto.io/quick-starts/next-auth)\n * - [Integrate Logto in your application](https://docs.logto.io/integrate-logto/integrate-logto-into-your-application)\n *\n * ### Notes\n *\n * The Logto provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/logto.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::info\n * By default, Auth.js assumes that the Logto provider is based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) spec\n * :::\n *\n * ## Help\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n */\nexport default function Logto(\n  options: OIDCUserConfig<LogtoProfile>\n): OIDCConfig<LogtoProfile> {\n  return {\n    id: \"logto\",\n    name: \"Logto\",\n    type: \"oidc\",\n    authorization: {\n      params: {\n        scope: \"offline_access openid email profile\",\n      },\n    },\n    profile(profile) {\n      return {\n        id: profile.sub,\n        name: profile.name ?? profile.username,\n        email: profile.email,\n        image: profile.picture,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/loops.ts",
    "content": "/**\n * <div style={{backgroundColor: \"#24292f\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Loops</b> integration.</span>\n * <a href=\"https://loops.so\">\n *  <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/loops.svg\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/loops\n */\n\nimport type { EmailConfig, EmailUserConfig } from \"./email.js\"\n\nexport type LoopsUserConfig = Omit<Partial<LoopsConfig>, \"options\" | \"type\">\n\nexport interface LoopsConfig\n  extends Omit<EmailConfig, \"sendVerificationRequest\" | \"options\"> {\n  id: string\n  apiKey: string\n  transactionalId: string\n  sendVerificationRequest: (params: Params) => Promise<void>\n  options: LoopsUserConfig\n}\n\ntype Params = Parameters<EmailConfig[\"sendVerificationRequest\"]>[0] & {\n  provider: LoopsConfig\n}\n\n/**\n *\n * @param config\n * @returns LoopsConfig\n * @requires LoopsUserConfig\n * @example\n * ```ts\n * Loops({\n *   apiKey: process.env.AUTH_LOOPS_KEY,\n *   transactionalId: process.env.AUTH_LOOPS_TRANSACTIONAL_ID,\n * })\n * ```\n *\n * @typedef LoopsUserConfig\n */\n\nexport default function Loops(config: LoopsUserConfig): LoopsConfig {\n  return {\n    id: \"loops\",\n    apiKey: \"\",\n    type: \"email\",\n    name: \"Loops\",\n    from: \"Auth.js <no-reply@authjs.dev>\",\n    maxAge: 24 * 60 * 60,\n    transactionalId: config.transactionalId || \"\",\n    async sendVerificationRequest(params: Params) {\n      const { identifier: to, provider, url } = params\n      if (!provider.apiKey || !provider.transactionalId)\n        throw new TypeError(\"Missing Loops API Key or TransactionalId\")\n\n      const res = await fetch(\"https://app.loops.so/api/v1/transactional\", {\n        method: \"POST\",\n        headers: {\n          Authorization: `Bearer ${provider.apiKey}`,\n          \"Content-Type\": \"application/json\",\n        },\n        body: JSON.stringify({\n          transactionalId: provider.transactionalId,\n          email: to,\n          dataVariables: {\n            url: url,\n          },\n        }),\n      })\n      if (!res.ok) {\n        throw new Error(\"Loops Send Error: \" + JSON.stringify(await res.json()))\n      }\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/mailchimp.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Mailchimp</b> integration.</span>\n * <a href=\"https://mailchimp.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/mailchimp.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/mailchimp\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add Mailchimp login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/mailchimp\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Mailchimp from \"@auth/core/providers/mailchimp\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Mailchimp({\n *       clientId: MAILCHIMP_CLIENT_ID,\n *       clientSecret: MAILCHIMP_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Mailchimp OAuth documentation](https://admin.mailchimp.com/account/oauth2/client/)\n *  - [Mailchimp documentation: Access user data](https://mailchimp.com/developer/marketing/guides/access-user-data-oauth-2/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Mailchimp provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Mailchimp provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/mailchimp.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Mailchimp(\n  config: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"mailchimp\",\n    name: \"Mailchimp\",\n    type: \"oauth\",\n    authorization: \"https://login.mailchimp.com/oauth2/authorize\",\n    token: \"https://login.mailchimp.com/oauth2/token\",\n    userinfo: \"https://login.mailchimp.com/oauth2/metadata\",\n    profile(profile) {\n      return {\n        id: profile.login.login_id,\n        name: profile.accountname,\n        email: profile.login.email,\n        image: null,\n      }\n    },\n    style: {\n      bg: \"#000\",\n      text: \"#fff\",\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/mailgun.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Mailgun</b> integration.</span>\n * <a href=\"https://www.mailgun.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/mailgun.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/mailgun\n */\nimport type { EmailConfig, EmailUserConfig } from \"./index.js\"\nimport { html, text } from \"../lib/utils/email.js\"\n\n/**\n * Add Mailgun login to your page.\n *\n * ### Setup\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Mailgun from \"@auth/core/providers/mailgun\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Mailgun({\n *       from: MAILGUN_DOMAIN,\n *       region: \"EU\", // Optional\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Mailgun documentation](https://documentation.mailgun.com/docs/mailgun)\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function MailGun(\n  config: EmailUserConfig & {\n    /**\n     * https://documentation.mailgun.com/docs/mailgun/api-reference/#base-url\n     *\n     * @default \"US\"\n     */\n    region?: \"US\" | \"EU\"\n  }\n): EmailConfig {\n  const { region = \"US\" } = config\n  const servers = {\n    US: \"api.mailgun.net\",\n    EU: \"api.eu.mailgun.net\",\n  }\n  const apiServer = servers[region]\n\n  return {\n    id: \"mailgun\",\n    type: \"email\",\n    name: \"Mailgun\",\n    from: \"Auth.js <no-reply@authjs.dev>\",\n    maxAge: 24 * 60 * 60,\n    async sendVerificationRequest(params) {\n      const { identifier: to, provider, url, theme } = params\n      const { host } = new URL(url)\n      const domain = provider.from?.split(\"@\").at(1)\n\n      if (!domain) throw new Error(\"malformed Mailgun domain\")\n\n      const form = new FormData()\n      form.append(\"from\", `${provider.name} <${provider.from}>`)\n      form.append(\"to\", to)\n      form.append(\"subject\", `Sign in to ${host}`)\n      form.append(\"html\", html({ host, url, theme }))\n      form.append(\"text\", text({ host, url }))\n\n      const res = await fetch(`https://${apiServer}/v3/${domain}/messages`, {\n        method: \"POST\",\n        headers: {\n          Authorization: `Basic ${btoa(`api:${provider.apiKey}`)}`,\n        },\n        body: form,\n      })\n\n      if (!res.ok) throw new Error(\"Mailgun error: \" + (await res.text()))\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/mailru.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Mailru</b> integration.</span>\n * <a href=\"https://mail.ru\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/mailru.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/mailru\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add Mailru login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/mailru\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Mailru from \"@auth/core/providers/mailru\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Mailru({ clientId: MAILRU_CLIENT_ID, clientSecret: MAILRU_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Mailru OAuth documentation](https://o2.mail.ru/docs)\n *  - [Mailru app console](https://o2.mail.ru/app/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Mailru provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Mailru provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/ma.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Mailru(\n  config: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"mailru\",\n    name: \"Mail.ru\",\n    type: \"oauth\",\n    authorization: \"https://oauth.mail.ru/login?scope=userinfo\",\n    token: \"https://oauth.mail.ru/token\",\n    userinfo: \"https://oauth.mail.ru/userinfo\",\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/mastodon.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Mastodon</b> integration.</span>\n * <a href=\"https://mastodon.social\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/mastodon.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/mastodon\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface MastodonProfile extends Record<string, any> {\n  id: string\n  username: string\n  acct: string\n  display_name: string\n  locked: boolean\n  bot: boolean\n  created_at: string\n  note: string\n  url: string\n  avatar: string\n  avatar_static: string\n  header: string\n  header_static: string\n  followers_count: number\n  following_count: number\n  statuses_count: number\n  last_status_at: string | null\n}\n\n/**\n * Add Mastodon login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/mastodon\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Mastodon from \"@auth/core/providers/mastodon\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Mastodon({\n *       clientId: MASTODON_CLIENT_ID,\n *       clientSecret: MASTODON_CLIENT_SECRET,\n *       issuer: MASTODON_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Mastodon OAuth documentation](https://docs.joinmastodon.org/client/token/)\n *  - [Mastodon OAuth Configuration](https://mastodon.social/settings/applications)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Mastodon provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * Due to Mastodons infrastructure beeing a Fediverse you have to define the `issuer` you want to connect to.\n *\n * :::tip\n *\n * The Mastodon provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/mastodon.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\n\nexport default function Mastodon<P extends MastodonProfile>(\n  options: OAuthUserConfig<P> & {\n    issuer: string\n  }\n): OAuthConfig<P> {\n  return {\n    id: \"mastodon\",\n    name: \"Mastodon\",\n    type: \"oauth\",\n    authorization: `${options.issuer}/oauth/authorize?scope=read`,\n    token: `${options.issuer}/oauth/token`,\n    userinfo: `${options.issuer}/api/v1/accounts/verify_credentials`,\n    profile(profile) {\n      return {\n        id: profile.id,\n        name: profile.username,\n        image: profile.avatar_static,\n        email: null,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/mattermost.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Mattermost</b> integration.</span>\n * <a href=\"https://mattermost.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/mattermost.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/mattermost\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./oauth.js\"\n\n/** [Get a user](https://api.mattermost.com/#tag/users/operation/GetUser) */\nexport interface MattermostProfile {\n  id: string\n  /** The time in milliseconds a user was created */\n  create_at: number\n  /** The time in milliseconds a user was last updated */\n  update_at: number\n  /** The time in milliseconds a user was deleted */\n  delete_at: number\n  username: string\n  auth_data: string\n  auth_service: string\n  email: string\n  email_verified: boolean\n  nickname: string\n  first_name: string\n  last_name: string\n  position: string\n  roles: string\n  notify_props: {\n    /** Set to \"true\" to enable channel-wide notifications (@channel, @all, etc.), \"false\" to disable. Defaults to \"true\". */\n    channel: string\n    comments: string\n    /** Set to \"all\" to receive desktop notifications for all activity, \"mention\" for mentions and direct messages only, and \"none\" to disable. Defaults to \"all\". */\n    desktop: string\n    /** Set to \"true\" to enable sound on desktop notifications, \"false\" to disable. Defaults to \"true\". */\n    desktop_sound: string\n    desktop_threads: string\n    /** Set to \"true\" to enable email notifications, \"false\" to disable. Defaults to \"true\". */\n    email: string\n    email_threads: string\n    /** Set to \"true\" to enable mentions for first name. Defaults to \"true\" if a first name is set, \"false\" otherwise. */\n    first_name: string\n    /** A comma-separated list of words to count as mentions. Defaults to username and @username. */\n    mention_keys: string\n    /** Set to \"all\" to receive push notifications for all activity, \"mention\" for mentions and direct messages only, and \"none\" to disable. Defaults to \"mention\". */\n    push: string\n    push_status: string\n    push_threads: string\n  }\n  last_password_update: number\n  locale: string\n  timezone: {\n    /** This value is set automatically when the \"useAutomaticTimezone\" is set to \"true\". */\n    automaticTimezone: string\n    /** Value when setting manually the timezone, i.e. \"Europe/Berlin\". */\n    manualTimezone: string\n    /** Set to \"true\" to use the browser/system timezone, \"false\" to set manually. Defaults to \"true\". */\n    useAutomaticTimezone: string\n  }\n  disable_welcome_email: boolean\n  /** ID of accepted terms of service, if any. This field is not present if empty. */\n  terms_of_service_id?: string\n  /** The time in milliseconds the user accepted the terms of service */\n  terms_of_service_create_at?: number\n}\n/**\n * Add Mattermost login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/mattermost\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Mattermost from \"@auth/core/providers/mattermost\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Mattermost({\n *       clientId: MATTERMOST_CLIENT_ID,\n *       clientSecret: MATTERMOST_CLIENT_SECRET,\n *       issuer: MATTERMOST_ISSUER, // The base url of your Mattermost instance. e.g `https://my-cool-server.cloud.mattermost.com`\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Mattermost OAuth documentation](https://example.com)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Mattermost provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * To create your Mattermost OAuth2 app visit `http://<your Mattermost instance url>/<your team>/integrations/oauth2-apps`\n *\n * :::warning\n *\n * The Mattermost provider requires the `issuer` option to be set. This is the base url of your Mattermost instance. e.g https://my-cool-server.cloud.mattermost.com\n *\n * :::\n *\n * :::tip\n *\n * The Mattermost provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/mattermost.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Mattermost<P extends MattermostProfile>(\n  config: OAuthUserConfig<P> & { issuer: string }\n): OAuthConfig<P> {\n  const { issuer, ...rest } = config\n\n  return {\n    id: \"mattermost\",\n    name: \"Mattermost\",\n    type: \"oauth\",\n    client: { token_endpoint_auth_method: \"client_secret_post\" },\n    token: `${issuer}/oauth/access_token`,\n    authorization: `${issuer}/oauth/authorize`,\n    userinfo: `${issuer}/api/v4/users/me`,\n    profile(profile) {\n      return {\n        id: profile.id,\n        name: profile.username ?? `${profile.first_name} ${profile.last_name}`,\n        email: profile.email,\n        image: null,\n      }\n    },\n    style: { bg: \"#000\", text: \"#fff\" },\n    options: rest,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/medium.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Medium</b> integration.</span>\n * <a href=\"https://medium.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/medium.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/medium\n */\n\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add Medium login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/medium\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Medium from \"@auth/core/providers/medium\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Medium({ clientId: MEDIUM_CLIENT_ID, clientSecret: MEDIUM_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Medium OAuth documentation](https://example.com)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Medium provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::warning\n *\n * Email address is not returned by the Medium API.\n *\n * :::\n *\n * :::tip\n *\n * The Medium provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/medium.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Medium(\n  config: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"medium\",\n    name: \"Medium\",\n    type: \"oauth\",\n    authorization: \"https://medium.com/m/oauth/authorize?scope=basicProfile\",\n    token: \"https://api.medium.com/v1/tokens\",\n    userinfo: \"https://api.medium.com/v1/me\",\n    profile(profile) {\n      return {\n        id: profile.data.id,\n        name: profile.data.name,\n        email: null,\n        image: profile.data.imageUrl,\n      }\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/microsoft-entra-id.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#0072c6\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Microsoft Entra ID</b> integration.</span>\n * <a href=\"https://learn.microsoft.com/en-us/entra/identity\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/microsoft-entra-id.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/microsoft-entra-id\n */\nimport { conformInternal, customFetch } from \"../lib/symbols.js\"\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\n/**\n * @see [Microsoft Identity Platform - ID token claims reference](https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference)\n * @see [Microsoft Identity Platform - Optional claims reference](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims-reference)\n */\nexport interface MicrosoftEntraIDProfile {\n  /**\n   * Identifies the intended recipient of the token. In `id_tokens`, the\n   * audience is your app's Application ID, assigned to your app in the Azure\n   * portal. This value should be validated. The token should be rejected if it\n   * fails to match your app's Application ID.\n   */\n  aud: string\n  /**\n   * Identifies the issuer, or \"authorization server\" that constructs and\n   * returns the token. It also identifies the tenant for which the user was\n   * authenticated. If the token was issued by the v2.0 endpoint, the URI ends\n   * in `/v2.0`. The GUID that indicates that the user is a consumer user from\n   * a Microsoft account is `9188040d-6c67-4c5b-b112-36a304b66dad`. Your app\n   * should use the GUID portion of the claim to restrict the set of tenants\n   * that can sign in to the app, if applicable.\t */\n  iss: string\n  /** Indicates when the authentication for the token occurred. */\n  iat: Date\n  /**\n   * Records the identity provider that authenticated the subject of the token.\n   * This value is identical to the value of the issuer claim unless the user\n   * account isn't in the same tenant as the issuer - guests, for instance. If\n   * the claim isn't present, it means that the value of `iss` can be used\n   * instead. For personal accounts being used in an organizational context\n   * (for instance, a personal account invited to a tenant), the `idp` claim\n   * may be 'live.com' or an STS URI containing the Microsoft account tenant\n   * `9188040d-6c67-4c5b-b112-36a304b66dad`.\n   */\n  idp: string\n  /**\n   * Identifies the time before which the JWT can't be accepted for processing.\n   */\n  nbf: Date\n  /**\n   * Identifies the expiration time on or after which the JWT can't be accepted\n   * for processing. In certain circumstances, a resource may reject the token\n   * before this time. For example, if a change in authentication is required\n   * or a token revocation has been detected.\n   */\n  exp: Date\n  /**\n   * The code hash is included in ID tokens only when the ID token is issued\n   * with an OAuth 2.0 authorization code. It can be used to validate the\n   * authenticity of an authorization code. To understand how to do this\n   * validation, see the\n   * [OpenID Connect specification](https://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken).\n   * This claim isn't returned on ID tokens from the /token endpoint.\n   */\n  c_hash: string\n  /**\n   * The access token hash is included in ID tokens only when the ID token is\n   * issued from the `/authorize` endpoint with an OAuth 2.0 access token. It\n   * can be used to validate the authenticity of an access token. To understand\n   * how to do this validation, see the\n   * [OpenID Connect specification](https://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken).\n   * This claim isn't returned on ID tokens from the `/token` endpoint.\n   */\n  at_hash: string\n  /**\n   * An internal claim that's used to record data for token reuse. Should be\n   * ignored.\n   */\n  aio: string\n  /**\n   * The primary username that represents the user. It could be an email\n   * address, phone number, or a generic username without a specified format.\n   * Its value is mutable and might change over time. Since it's mutable, this\n   * value can't be used to make authorization decisions. It can be used for\n   * username hints and in human-readable UI as a username. The `profile` scope\n   * is required to receive this claim. Present only in v2.0 tokens.\n   */\n  preferred_username: string\n  /**\n   * Present by default for guest accounts that have an email address. Your app\n   * can request the email claim for managed users (from the same tenant as the\n   * resource) using the `email`\n   * [optional claim](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims).\n   * This value isn't guaranteed to be correct and is mutable over time. Never\n   * use it for authorization or to save data for a user. If you require an\n   * addressable email address in your app, request this data from the user\n   * directly by using this claim as a suggestion or prefill in your UX. On the\n   * v2.0 endpoint, your app can also request the `email` OpenID Connect\n   * scope - you don't need to request both the optional claim and the scope to\n   * get the claim.\n   */\n  email: string\n  /**\n   * The `name` claim provides a human-readable value that identifies the\n   * subject of the token. The value isn't guaranteed to be unique, it can be\n   * changed, and should be used only for display purposes. The `profile` scope\n   * is required to receive this claim.\n   */\n  name: string\n  /**\n   * The nonce matches the parameter included in the original authorize request\n   * to the IDP. If it doesn't match, your application should reject the token.\n   */\n  nonce: string\n  /**\n   * The immutable identifier for an object, in this case, a user account. This\n   * ID uniquely identifies the user across applications - two different\n   * applications signing in the same user receives the same value in the `oid`\n   * claim. Microsoft Graph returns this ID as the `id` property for a user\n   * account. Because the `oid` allows multiple apps to correlate users, the\n   * `profile` scope is required to receive this claim. If a single user exists\n   * in multiple tenants, the user contains a different object ID in each\n   * tenant - they're considered different accounts, even though the user logs\n   * into each account with the same credentials. The `oid` claim is a GUID and\n   * can't be reused.\n   */\n  oid: string\n  /** The set of roles that were assigned to the user who is logging in. */\n  roles: string[]\n  /** An internal claim used to revalidate tokens. Should be ignored. */\n  rh: string\n  /**\n   * The subject of the information in the token. For example, the user of an\n   * app. This value is immutable and can't be reassigned or reused. The\n   * subject is a pairwise identifier and is unique to an application ID. If a\n   * single user signs into two different apps using two different client IDs,\n   * those apps receive two different values for the subject claim. You may or\n   * may not want two values depending on your architecture and privacy\n   * requirements.\n   */\n  sub: string\n  /** Represents the tenant that the user is signing in to. For work and school\n   * accounts, the GUID is the immutable tenant ID of the organization that the\n   * user is signing in to. For sign-ins to the personal Microsoft account\n   * tenant (services like Xbox, Teams for Life, or Outlook), the value is\n   * `9188040d-6c67-4c5b-b112-36a304b66dad`.\n   */\n  tid: string\n  /**\n   * Represents an unique identifier for a session and will be generated when a\n   * new session is established.\n   */\n  sid: string\n  /**\n   * Token identifier claim, equivalent to jti in the JWT specification.\n   * Unique, per-token identifier that is case-sensitive.\n   */\n  uti: string\n  /** Indicates the version of the ID token. */\n  ver: \"2.0\"\n  /**\n   * If present, always true, denoting the user is in at least one group.\n   * Indicates that the client should use the Microsoft Graph API to determine\n   * the user's groups\n   * (`https://graph.microsoft.com/v1.0/users/{userID}/getMemberObjects`).\n   */\n  hasgroups: boolean\n  /**\n   * Users account status in tenant. If the user is a member of the tenant, the\n   * value is `0`. If they're a guest, the value is `1`.\n   */\n  acct: 0 | 1\n  /**\n   * Auth Context IDs. Indicates the Auth Context IDs of the operations that\n   * the bearer is eligible to perform. Auth Context IDs can be used to trigger\n   * a demand for step-up authentication from within your application and\n   * services. Often used along with the `xms_cc` claim.\n   */\n  acrs: string\n  /** Time when the user last authenticated. */\n  auth_time: Date\n  /**\n   * User's country/region. This claim is returned if it's present and the\n   * value of the field is a standard two-letter country/region code, such as\n   * FR, JP, SZ, and so on.\n   */\n  ctry: string\n  /**\n   * IP address. Adds the original address of the requesting client\n   * (when inside a VNET).\n   */\n  fwd: string\n  /**\n   * Optional formatting for group claims. The `groups` claim is used with the\n   * GroupMembershipClaims setting in the\n   * [application manifest](https://learn.microsoft.com/en-us/entra/identity-platform/reference-app-manifest),\n   * which must be set as well.\n   */\n  groups: string\n  /**\n   * Login hint. An opaque, reliable login hint claim that's base 64 encoded.\n   * Don't modify this value. This claim is the best value to use for the\n   * `login_hint` OAuth parameter in all flows to get SSO. It can be passed\n   * between applications to help them silently SSO as well - application A can\n   * sign in a user, read the `login_hint` claim, and then send the claim and\n   * the current tenant context to application B in the query string or\n   * fragment when the user selects on a link that takes them to application B.\n   * To avoid race conditions and reliability issues, the `login_hint` claim\n   * doesn't include the current tenant for the user, and defaults to the\n   * user's home tenant when used. In a guest scenario where the user is from\n   * another tenant, a tenant identifier must be provided in the sign-in\n   * request. and pass the same to apps you partner with. This claim is\n   * intended for use with your SDK's existing `login_hint` functionality,\n   * however that it exposed.\n   */\n  login_hint: string\n  /**\n   * Resource tenant's country/region. Same as `ctry` except set at a tenant\n   * level by an admin. Must also be a standard two-letter value.\n   */\n  tenant_ctry: string\n  /**\n   * Region of the resource tenant\n   */\n  tenant_region_scope: string\n  /**\n   * UserPrincipalName. An identifier for the user that can be used with the\n   * `username_hint` parameter. Not a durable identifier for the user and\n   * shouldn't be used for authorization or to uniquely identity user\n   * information (for example, as a database key). Instead, use the user object\n   * ID (`oid`) as a database key. For more information, see\n   * [Secure applications and APIs by validating claims](https://learn.microsoft.com/en-us/entra/identity-platform/claims-validation).\n   * Users signing in with an\n   * [alternate login ID](https://learn.microsoft.com/en-us/entra/identity/authentication/howto-authentication-use-email-signin)\n   * shouldn't be shown their User Principal Name (UPN). Instead, use the\n   * following ID token claims for displaying sign-in state to the user:\n   * `preferred_username` or `unique_name` for v1 tokens and\n   * `preferred_username` for v2 tokens. Although this claim is automatically\n   * included, you can specify it as an optional claim to attach other\n   * properties to modify its behavior in the guest user case. You should use\n   * the `login_hint` claim for `login_hint` use - human-readable identifiers\n   * like UPN are unreliable.\n   */\n  upn: string\n  /** Sourced from the user's PrimaryAuthoritativeEmail */\n  verified_primary_email: string[]\n  /** Sourced from the user's SecondaryAuthoritativeEmail */\n  verified_secondary_email: string[]\n  /** VNET specifier information. */\n  vnet: string\n  /**\n   * Client Capabilities. Indicates whether the client application that\n   * acquired the token is capable of handling claims challenges. It's often\n   * used along with claim `acrs`. This claim is commonly used in Conditional\n   * Access and Continuous Access Evaluation scenarios. The resource server or\n   * service application that the token is issued for controls the presence of\n   * this claim in a token. A value of `cp1` in the access token is the\n   * authoritative way to identify that a client application is capable of\n   * handling a claims challenge. For more information, see\n   * [Claims challenges, claims requests and client capabilities](https://learn.microsoft.com/en-us/entra/identity-platform/claims-challenge?tabs=dotnet).\n   */\n  xms_cc: string\n  /**\n   * Boolean value indicating whether the user's email domain owner has been\n   * verified. An email is considered to be domain verified if it belongs to\n   * the tenant where the user account resides and the tenant admin has done\n   * verification of the domain. Also, the email must be from a Microsoft\n   * account (MSA), a Google account, or used for authentication using the\n   * one-time passcode (OTP) flow. Facebook and SAML/WS-Fed accounts do not\n   * have verified domains. For this claim to be returned in the token, the\n   * presence of the `email` claim is required.\n   */\n  xms_edov: boolean\n  /**\n   * Preferred data location. For Multi-Geo tenants, the preferred data\n   * location is the three-letter code showing the geographic region the user\n   * is in. For more information, see the\n   * [Microsoft Entra Connect documentation about preferred data location](https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/how-to-connect-sync-feature-preferreddatalocation).\n   */\n  xms_pdl: string\n  /**\n   * User preferred language. The user's preferred language, if set. Sourced\n   * from their home tenant, in guest access scenarios. Formatted LL-CC\n   * (\"en-us\").\n   */\n  xms_pl: string\n  /**\n   * Tenant preferred language. The resource tenant's preferred language, if\n   * set. Formatted LL (\"en\").\n   */\n  xms_tpl: string\n  /**\n   * Zero-touch Deployment ID. The device identity used for `Windows AutoPilot`.\n   */\n  ztdid: string\n  /** IP Address. The IP address the client logged in from. */\n  ipaddr: string\n  /** On-premises Security Identifier */\n  onprem_sid: string\n  /**\n   * Password Expiration Time. The number of seconds after the time in the\n   * `iat` claim at which the password expires. This claim is only included\n   * when the password is expiring soon (as defined by \"notification days\" in\n   * the password policy).\n   */\n  pwd_exp: number\n  /**\n   * Change Password URL. A URL that the user can visit to change their\n   * password. This claim is only included when the password is expiring soon\n   * (as defined by \"notification days\" in the password policy).\n   */\n  pwd_url: string\n  /**\n   * Inside Corporate Network. Signals if the client is logging in from the\n   * corporate network. If they're not, the claim isn't included. Based off of\n   * the\n   * [trusted IPs](https://learn.microsoft.com/en-us/entra/identity/authentication/howto-mfa-mfasettings#trusted-ips)\n   * settings in MFA.\n   */\n  in_corp: string\n  /**\n   * Last Name. Provides the last name, surname, or family name of the user as\n   * defined in the user object. For example, `\"family_name\":\"Miller\"`.\n   * Supported in MSA and Microsoft Entra ID. Requires the `profile` scope.\n   */\n  family_name: string\n  /**\n   * First name. Provides the first or \"given\" name of the user, as set on the\n   * user object. For example, `\"given_name\": \"Frank\"`. Supported in MSA and\n   * Microsoft Entra ID. Requires the `profile` scope.\n   */\n  given_name: string\n}\n\n/**\n * ### Setup\n *\n * #### Callback URL\n *\n * ```\n * https://example.com/api/auth/callback/microsoft-entra-id\n * ```\n *\n * #### Environment Variables\n *\n * ```env\n * AUTH_MICROSOFT_ENTRA_ID_ID=\"<Application (client) ID>\"\n * AUTH_MICROSOFT_ENTRA_ID_SECRET=\"<Client secret value>\"\n * AUTH_MICROSOFT_ENTRA_ID_ISSUER=\"https://login.microsoftonline.com/<Directory (tenant) ID>/v2.0/\"\n * ```\n *\n * #### Configuration\n *\n * When the `issuer` parameter is omitted it will default to\n * `\"https://login.microsoftonline.com/common/v2.0/\"`.\n * This allows any Microsoft account (Personal, School or Work) to log in.\n *\n * ```typescript\n * import MicrosoftEntraID from \"@auth/core/providers/microsoft-entra-id\"\n * ...\n * providers: [\n *   MicrosoftEntraID({\n *     clientId: process.env.AUTH_MICROSOFT_ENTRA_ID_ID,\n *     clientSecret: process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET,\n *   }),\n * ]\n * ...\n * ```\n *\n * To only allow your organization's users to log in you will need to configure\n * the `issuer` parameter with your Directory (tenant) ID.\n *\n * ```env\n * AUTH_MICROSOFT_ENTRA_ID_ISSUER=\"https://login.microsoftonline.com/<Directory (tenant) ID>/v2.0/\"\n * ```\n *\n * ```typescript\n * import MicrosoftEntraID from \"@auth/core/providers/microsoft-entra-id\"\n * ...\n * providers: [\n *   MicrosoftEntraID({\n *     clientId: process.env.AUTH_MICROSOFT_ENTRA_ID_ID,\n *     clientSecret: process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET,\n *     issuer: process.env.AUTH_MICROSOFT_ENTRA_ID_ISSUER,\n *   }),\n * ]\n * ...\n * ```\n *\n * ### Resources\n *\n *  - [Microsoft Entra OAuth documentation](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow)\n *  - [Microsoft Entra OAuth apps](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app)\n *\n * ### Notes\n *\n * Microsoft Entra ID returns the profile picture in an ArrayBuffer, instead of\n * just a URL to the image, so our provider converts it to a base64 encoded\n * image string and returns that instead. See:\n * https://learn.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0&tabs=http#examples.\n * The default image size is 48x48 to avoid\n * [running out of space](https://next-auth.js.org/faq#json-web-tokens)\n * in case the session is saved as a JWT.\n *\n * By default, Auth.js assumes that the Microsoft Entra ID provider is based on\n * the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html)\n * specification.\n *\n * :::tip\n *\n * The Microsoft Entra ID provider comes with a\n * [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/microsoft-entra-id.ts).\n * To override the defaults for your use case, check out\n * [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can\n * [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take\n * responsibility for any deviation from the spec by the provider. You can open\n * an issue, but if the problem is non-compliance with the spec, we might not\n * pursue a resolution. You can ask for more help in\n * [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function MicrosoftEntraID(\n  config: OIDCUserConfig<MicrosoftEntraIDProfile> & {\n    /**\n     * https://learn.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0&tabs=http#examples\n     *\n     * @default 48\n     */\n    profilePhotoSize?: 48 | 64 | 96 | 120 | 240 | 360 | 432 | 504 | 648\n  }\n): OIDCConfig<MicrosoftEntraIDProfile> {\n  const { profilePhotoSize = 48 } = config\n\n  // If issuer is not set, first fallback to environment variable, then\n  // fallback to /common/ uri.\n  config.issuer ??=\n    process.env.AUTH_MICROSOFT_ENTRA_ID_ISSUER ||\n    \"https://login.microsoftonline.com/common/v2.0\"\n\n  return {\n    id: \"microsoft-entra-id\",\n    name: \"Microsoft Entra ID\",\n    type: \"oidc\",\n    authorization: { params: { scope: \"openid profile email User.Read\" } },\n    async profile(profile, tokens) {\n      // https://learn.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0&tabs=http#examples\n      const response = await fetch(\n        `https://graph.microsoft.com/v1.0/me/photos/${profilePhotoSize}x${profilePhotoSize}/$value`,\n        { headers: { Authorization: `Bearer ${tokens.access_token}` } }\n      )\n\n      // Confirm that profile photo was returned\n      let image\n      // TODO: Do this without Buffer\n      if (response.ok && typeof Buffer !== \"undefined\") {\n        try {\n          const pictureBuffer = await response.arrayBuffer()\n          const pictureBase64 = Buffer.from(pictureBuffer).toString(\"base64\")\n          image = `data:image/jpeg;base64, ${pictureBase64}`\n        } catch {}\n      }\n\n      return {\n        id: profile.sub,\n        name: profile.name,\n        email: profile.email,\n        image: image ?? null,\n      }\n    },\n    style: { text: \"#fff\", bg: \"#0072c6\" },\n    async [customFetch](...args) {\n      const url = new URL(args[0] instanceof Request ? args[0].url : args[0])\n      if (url.pathname.endsWith(\".well-known/openid-configuration\")) {\n        const response = await fetch(...args)\n        const json = await response.clone().json()\n        const tenantRe = /microsoftonline\\.com\\/(\\w+)\\/v2\\.0/\n        const tenantId = config.issuer?.match(tenantRe)?.[1] ?? \"common\"\n        const issuer = json.issuer.replace(\"{tenantid}\", tenantId)\n        return Response.json({ ...json, issuer })\n      }\n      return fetch(...args)\n    },\n    [conformInternal]: true,\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/naver.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Naver</b> integration.</span>\n * <a href=\"https://naver.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/naver.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/naver\n */\n\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/** https://developers.naver.com/docs/login/profile/profile.md */\nexport interface NaverProfile extends Record<string, any> {\n  resultcode: string\n  message: string\n  response: {\n    id: string\n    nickname?: string\n    name?: string\n    email?: string\n    gender?: \"F\" | \"M\" | \"U\"\n    age?: string\n    birthday?: string\n    profile_image?: string\n    birthyear?: string\n    mobile?: string\n  }\n}\n\n/**\n * Add Naver login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/naver\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Naver from \"@auth/core/providers/naver\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Naver({ clientId: NAVER_CLIENT_ID, clientSecret: NAVER_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Naver OAuth documentation](https://developers.naver.com/docs/login/overview/overview.md)\n *  - [Naver OAuth documentation 2](https://developers.naver.com/docs/login/api/api.md)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Naver provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Naver provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/naver.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Naver<P extends NaverProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"naver\",\n    name: \"Naver\",\n    type: \"oauth\",\n    authorization: \"https://nid.naver.com/oauth2.0/authorize\",\n    token: \"https://nid.naver.com/oauth2.0/token\",\n    userinfo: \"https://openapi.naver.com/v1/nid/me\",\n    profile(profile) {\n      return {\n        id: profile.response.id,\n        name: profile.response.nickname,\n        email: profile.response.email,\n        image: profile.response.profile_image,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/netlify.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Netlify</b> integration.</span>\n * <a href=\"https://netlify.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/netlify.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/netlify\n */\n\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add Netlify login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/netlify\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Netlify from \"@auth/core/providers/netlify\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Netlify({\n *       clientId: NETLIFY_CLIENT_ID,\n *       clientSecret: NETLIFY_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Netlify OAuth blog](https://www.netlify.com/blog/2016/10/10/integrating-with-netlify-oauth2/)\n *  - [Netlify OAuth example](https://github.com/netlify/netlify-oauth-example/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Netlify provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Netlify provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/netlify.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Netlify(\n  config: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"netlify\",\n    name: \"Netlify\",\n    type: \"oauth\",\n    authorization: \"https://app.netlify.com/authorize?scope\",\n    token: \"https://api.netlify.com/oauth/token\",\n    userinfo: \"https://api.netlify.com/api/v1/user\",\n    profile(profile) {\n      return {\n        id: profile.id,\n        name: profile.full_name,\n        email: profile.email,\n        image: profile.avatar_url,\n      }\n    },\n    style: {\n      brandColor: \"#32e6e2\",\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/netsuite.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#24292f\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>NetSuite</b> integration.</span>\n * <a href=\"https://system.netsuite.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/netsuite.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/netsuite\n */\n\n/*\n * This NetSuite provider uses OAuth 2 Features. Ensure you have an integration record and access token set up in order to use this provider.\n * Read more about OAuth 2 setup here: https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_157771281570.html\n */\n\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface OAuthNetSuiteOptions {\n  /**\n   *  The prompt options - also viewable below\n   *\n   *  @link https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_160855585734.html\n   *\n   * \tauthorization.params.prompt\n   *\n   * The optional prompt parameter provides additional control of when the login/consent screen appears. Following are the values you can use with the prompt parameter:\n   * \"none\" - the consent screen does not appear. If there is no active session, the application returns an error.\n   * \"login\" - the user must authenticate even if there is an active session.\n   * This option only works if the application sends the request to the account-specific domain.\n   * \"consent\" - the consent screen appears every time. The user must authenticate if there is no active session.\n   * login consent or consent login - the consent screen appears every time, and the user must authenticate even if there is an active session and allow the connection to the NetSuite. Similar to GitHub, Google, and Facebook data consent screens.\n   */\n  prompt: string | \"none\" | \"login\" | \"consent\"\n  /**\n   * EX: TSTDRV1234567 or 81555 for prod\n   */\n  accountID: string\n  /**\n   * restlets rest_webservices or restlets or rest_webservices suiteanalytics_connect restlets\n   */\n  scope: string\n  /**\n   * Either a restlet or suitelet returning runtime info or record info -> RESTlet recommended\n   */\n  userinfo: string\n}\n\nexport interface NetSuiteProfile {\n  // Main N/runtime.getCurrentUser() object return\n  id: number\n  name: string\n  email: string\n  location: number\n  role: number\n  roleId?: string\n  roleCenter?: string\n  contact?: number\n  subsidiary?: number\n  department?: number\n}\n\n/**\n * Add Netsuite login to your page and make requests to:\n * - [NetSuite RESTLets](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_4567507062.html#Tracking-RESTlet-Calls-Made-with-TBA-and-OAuth-2.0).\n * - [NetSuite REST Web Services](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/book_1559132836.html#SuiteTalk-REST-Web-Services-API-Guide).\n *\n * ### Setup\n *\n * #### Disclaimer\n * By using this provider, you consent to sharing your data with NetSuite.\n * By using this provider we assume you comply with NetSuite's [Terms of Service](https://www.netsuite.com/portal/assets/pdf/terms_of_service.pdf) and [Privacy Policy](https://www.oracle.com/legal/privacy).\n * The author of this provider is not affiliated with NetSuite. Proceeding with this provider you must be a NetSuite customer and have a NetSuite account (Full access user).\n * **Ensure the OAuth 2.0 Feature is enabled in your NetSuite account with the proper permissions set up on the current role/user**\n *\n * Before setting up the provider, you will need to:\n * - [Create an Integration Record](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_157771733782.html#procedure_157838925981)\n *   - Uncheck the TBA Auth Flow checkbox.\n *   - Check OAuth 2.0 Auth Flow checkbox.\n *   - Copy and paste the `Callback URL` below into the `Redirect URI` field.\n *   - Then select the scope(s) you want to use.\n *     - **REST Web Services** (`rest_webservices`) - Access to REST Web Services.\n *     - **RESTlets**(`restlets`) - Access to RESTLets.\n *     - **SuiteAnalytics Connect** (`suiteanalytics_connect`) - Access to SuiteAnalytics Connect.\n *   - Add any policies you want to use.\n *     - Application Logo (_Optional_) (Shown to users when they are asked to grant access to your application). - Consent Screen\n *     - Application Terms of Use (_Optional_) - A PDF file that contains the terms of use for your application. - Consent Screen\n *     - Application Privacy Policy (_Optional_) - A PDF file that contains the privacy policy for your application. - Consent Screen\n *   - OAuth 2.0 Consent Policy Preference - This setting determines whether the user is asked to grant access to your application **every time** they sign in or only the **first time** they sign in or **never**.\n *   - **Save** the Integration record.\n *   - The Integration record will be used to generate the `clientId` and `clientSecret` for the provider. **Save the generated values for later**\n *\n * #### Callback URL\n *\n * :::tip\n * When setting the Redirect URI in the Integration record, you must use the `https` protocol.\n * Otherwise, you will get an error when trying to sign in. (_INVALID_LOGIN_ATTEMPT_).\n * If you are testing locally, you can use a service like [ngrok](https://ngrok.com/) to create a secure tunnel to your localhost.\n * :::\n *\n * ```\n * https://example.com/api/auth/callback/netsuite\n * ```\n *\n * :::tip\n * Our `userinfo` needs to compose of a suitelet or RESTLet url that gives us the information about the user. This has to be very fast in which the handshake profile gather execution can't take long.\n * The best bet is to use the `N/runtime` module to get the basics first. - Here is an example of a RESTlet below. Be sure to deploy and enable access to \"All Roles\".\n * :::\n *\n * #### Example RESTLet Callback Handler\n * Be sure to deploy and use the **external** RESTLet url of any usage of the URIs.\n *\n * ```js\n * * /**\n * * @NApiVersion 2.1\n * * @NScriptType Restlet\n * *\\/\n * define([\"N/runtime\"], /**\n *  @param{runtime} runtimee\n * \\/ (runtime) => {\n *  /**\n *   * Defines the function that is executed when a GET request is sent to a RESTlet.\n *   * @param {Object} requestParams - Parameters from HTTP request URL; parameters passed as an Object (for all supported\n *   *     content types)\n *   * @returns {string | Object} HTTP response body; returns a string when request Content-Type is 'text/plain'; returns an\n *   *     Object when request Content-Type is 'application/json' or 'application/xml'\n *   * @since 2015.2\n *   *\\/\n *   const get = (requestParams) => {\n *     let userObject = runtime.getCurrentUser();\n *\n *     try {\n *       log.debug({ title: \"Payload received:\", details: requestParams });\n *\n *       const { id, name, role, location, email, contact } = userObject;\n *\n *       log.audit({ title: \"Current User Ran\", details: name });\n *\n *       let user = {\n *         id,\n *         name,\n *         role,\n *         location,\n *         email,\n *         contact,\n *       };\n *\n *       log.debug({ title: \"Returning user\", details: user });\n *\n *       return JSON.stringify(user);\n *     } catch (e) {\n *       log.error({ title: \"Error grabbing current user:\", details: e });\n *     }\n *   };\n *\n *   return {\n *     get,\n *   };\n * );\n * ```\n *\n * > **Note**: Above is an example of returning the basic runtime information. Be sure to create a new script record and deployment record. Upon saving the deployment record. We will get our URLs for our RESTlet.\n *\n * ### Configuration\n *\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Netsuite from \"@auth/core/providers/netsuite\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *       NetSuite({\n *         accountID: NETSUITE_ACCOUNT_ID, // EX: TSTDRV1234567 or 81555 for prod, and 1234567-SB1 for Sandbox accounts not \"_\" use \"-\".\n *        // Returns the current user using the N/runtime module. This url can be a suitelet or RESTlet (Recommended)\n *        // Using getCurrentUser(); So we match this schema returned from this RESTlet in the profile callback. (Required)\n *         userinfo: \"https://1234567.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=123&deploy=1\",\n *       })\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [NetSuite - Creating an Integration Record (OAuth 2.0)](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_157771733782.html#Related-Topics)\n * - [NetSuite - Authorizing OAuth Requests](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps)\n * - [NetSuite - Configure OAuth Roles](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_157771510070.html#Set-Up-OAuth-2.0-Roles)\n * - [Learn more about NetSuite OAuth 2.0](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/chapter_157769826287.html#OAuth-2.0)\n *\n * ### Notes\n *\n * :::tip\n * Make sure the `userinfo` matches the return type of the profile callback to ensure the user session gets read correctly.\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).\n * :::\n *\n */\nexport default function NetSuite<P extends NetSuiteProfile>(\n  config: OAuthUserConfig<P> & OAuthNetSuiteOptions\n): OAuthConfig<P> {\n  const { accountID } = config\n\n  return {\n    id: \"netsuite\",\n    name: \"NetSuite\",\n    type: \"oauth\",\n    checks: [\"state\"],\n    authorization: {\n      url: `https://${accountID}.app.netsuite.com/app/login/oauth2/authorize.nl`,\n      params: { scope: \"restlets rest_webservices\" },\n    },\n    token: `https://${accountID}.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token`,\n    profile(profile) {\n      // This is the default runtime.getCurrentUser() object returned from the RESTlet or SUITELet\n      return {\n        id: profile.id.toString(),\n        name: profile.name,\n        email: profile.email,\n        image: null,\n      }\n    },\n    style: { logo: \"/netsuite.svg\", bg: \"#181a1b\", text: \"#fbfbfb\" },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/nextcloud.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#0082C9\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Nextcloud</b> integration.</span>\n * <a href=\"https://nextcloud.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/nextcloud.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/nextcloud\n */\n\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Represents the Nextcloud user profile data returned from the `/ocs/v1.php/cloud/users/`.\n * @see [Check out the documentation for more details](https://docs.nextcloud.com/server/latest/admin_manual/configuration_user/instruction_set_for_users.html#get-data-of-a-single-user)\n */\nexport interface NextcloudProfile extends Record<string, any> {\n  /**\n   * The user's username.\n   * @example \"frank\"\n   */\n  id: string\n\n  /**\n   * The email address associated with the user.\n   * @example \"frank@domain.tld\"\n   */\n  email: string | null\n\n  /**\n   * The display name of the user.\n   * @example \"Frank K.\"\n   */\n  displayname: string\n\n  /**\n   * The phone number of the user.\n   */\n  phone: string\n\n  /**\n   * The address of the user.\n   * @example \"Foobar 12, 12345 Town\"\n   */\n  address: string\n\n  /**\n   * The website URL of the user.\n   * @example \"https://nextcloud.com\"\n   */\n  website: string\n\n  /**\n   * The user's Twitter handle.\n   * @example \"Nextcloud\"\n   */\n  twitter: string\n\n  /**\n   * The user's Fediverse handle.\n   */\n  fediverse: string\n\n  /**\n   * The organization associated with the user.\n   */\n  organisation: string\n\n  /**\n   * The role or position of the user.\n   */\n  role: string\n\n  /**\n   * The headline or brief description of the user.\n   */\n  headline: string\n\n  /**\n   * The biography or detailed description of the user.\n   */\n  biography: string\n\n  /**\n   * An array of group names that the user belongs to.\n   * @example [\"admin\", \"group1\", \"group2\"]\n   */\n  groups: string[]\n\n  /**\n   * The language preference of the user.\n   * @example \"en\"\n   */\n  language: string\n\n  /**\n   * The locale or language locale of the user.\n   * @example \"en_US\"\n   */\n  locale: string\n\n  /**\n   * Indicates whether the user account is enabled or disabled.\n   * @example true\n   */\n  enabled: boolean\n\n  /**\n   * The storage location of the user's files.\n   * @example \"/path/to/nextcloud/data/frank\"\n   */\n  storageLocation: string\n}\n\n/**\n * Add Nextcloud login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/auth/callback/nextcloud\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Nextcloud from \"@auth/core/providers/nextcloud\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Nextcloud({ clientId: AUTH_NEXTCLOUD_ID, clientSecret: AUTH_NEXTCLOUD_SECRET, issuer: AUTH_NEXTCLOUD_ISSUER }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Nextcloud Documentation](https://docs.nextcloud.com/)\n * - [Nextcloud OAuth 2](https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/oauth2.html)\n * - [Nextcloud Clients and Client APIs](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/index.html)\n * - [Nextcloud User provisioning API](https://docs.nextcloud.com/server/latest/admin_manual/configuration_user/user_provisioning_api.html)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Nextcloud provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Nextcloud provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/nextcloud.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Nextcloud(\n  options: OAuthUserConfig<NextcloudProfile>\n): OAuthConfig<NextcloudProfile> {\n  return {\n    id: \"nextcloud\",\n    name: \"Nextcloud\",\n    type: \"oauth\",\n    authorization: `${options.issuer}/apps/oauth2/authorize`,\n    token: `${options.issuer}/apps/oauth2/api/v1/token`,\n    userinfo: {\n      url: `${options.issuer}/ocs/v1.php/cloud/users`,\n      async request({ tokens, provider }) {\n        const url = `${provider.userinfo?.url}/${tokens.user_id}`\n\n        const res = await fetch(url, {\n          headers: {\n            \"OCS-APIRequest\": \"true\",\n            Authorization: `Bearer ${tokens.access_token}`,\n            Accept: \"application/json\",\n          },\n        }).then((res) => res.json())\n        return res.ocs.data\n      },\n    },\n    profile(profile) {\n      return {\n        id: profile.id,\n        name: profile.displayname,\n        email: profile.email,\n        image: `${options.issuer}/avatar/${profile.id}/512`,\n      }\n    },\n    style: {\n      logo: \"/nextcloud.svg\",\n      bg: \"#fff\",\n      text: \"#0082C9\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/nodemailer.ts",
    "content": "import { createTransport } from \"nodemailer\"\nimport { AuthError } from \"../errors.js\"\nimport { html, text } from \"../lib/utils/email.js\"\n\nimport type { Transport, TransportOptions } from \"nodemailer\"\nimport * as JSONTransport from \"nodemailer/lib/json-transport/index.js\"\nimport * as SendmailTransport from \"nodemailer/lib/sendmail-transport/index.js\"\nimport * as SESTransport from \"nodemailer/lib/ses-transport/index.js\"\nimport * as SMTPPool from \"nodemailer/lib/smtp-pool/index.js\"\nimport * as SMTPTransport from \"nodemailer/lib/smtp-transport/index.js\"\nimport * as StreamTransport from \"nodemailer/lib/stream-transport/index.js\"\nimport type { Awaitable, Theme } from \"../types.js\"\nimport type { EmailConfig } from \"./email.js\"\n\ntype AllTransportOptions =\n  | string\n  | SMTPTransport\n  | SMTPTransport.Options\n  | SMTPPool\n  | SMTPPool.Options\n  | SendmailTransport\n  | SendmailTransport.Options\n  | StreamTransport\n  | StreamTransport.Options\n  | JSONTransport\n  | JSONTransport.Options\n  | SESTransport\n  | SESTransport.Options\n  | Transport<any>\n  | TransportOptions\n\nexport interface NodemailerConfig extends EmailConfig {\n  server?: AllTransportOptions\n  sendVerificationRequest: (params: {\n    identifier: string\n    url: string\n    expires: Date\n    provider: NodemailerConfig\n    token: string\n    theme: Theme\n    request: Request\n  }) => Awaitable<void>\n  options?: NodemailerUserConfig\n}\n\nexport type NodemailerUserConfig = Omit<\n  Partial<NodemailerConfig>,\n  \"options\" | \"type\"\n>\n\nexport default function Nodemailer(\n  config: NodemailerUserConfig\n): NodemailerConfig {\n  if (!config.server)\n    throw new AuthError(\"Nodemailer requires a `server` configuration\")\n\n  return {\n    id: \"nodemailer\",\n    type: \"email\",\n    name: \"Nodemailer\",\n    server: { host: \"localhost\", port: 25, auth: { user: \"\", pass: \"\" } },\n    from: \"Auth.js <no-reply@authjs.dev>\",\n    maxAge: 24 * 60 * 60,\n    async sendVerificationRequest(params) {\n      const { identifier, url, provider, theme } = params\n      const { host } = new URL(url)\n      const transport = createTransport(provider.server)\n      const result = await transport.sendMail({\n        to: identifier,\n        from: provider.from,\n        subject: `Sign in to ${host}`,\n        text: text({ url, host }),\n        html: html({ url, host, theme }),\n      })\n      const rejected = result.rejected || []\n      const pending = result.pending || []\n      const failed = rejected.concat(pending).filter(Boolean)\n      if (failed.length) {\n        throw new Error(`Email (${failed.join(\", \")}) could not be sent`)\n      }\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/notion.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Notion</b> integration.</span>\n * <a href=\"https://notion.so\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/notion.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/notion\n */\n\nimport type { OAuthConfig, OAuthUserConfig } from \"./oauth.js\"\n\nexport interface Person extends Record<string, any> {\n  email: string\n}\n\n// https://developers.notion.com/reference/user\nexport interface User extends Record<string, any> {\n  object: \"user\" | \"bot\"\n  id: string\n  type: string\n  name: string\n  avatar_url: null | string\n  person: Person\n  owner?: {\n    type: \"workspace\" | \"user\"\n    workspace: string\n  }\n  workspace_name?: string | null\n}\n\nexport interface Owner {\n  type: string\n  user: User\n}\n\n// Notion responds with an access_token + some additional information, which we define here\n// More info -  https://developers.notion.com/docs/authorization#step-4-notion-responds-with-an-access_token-and-some-additional-information\nexport interface NotionProfile extends Record<string, any> {\n  access_token: string\n  bot_id: string\n  duplicated_template_id: string\n  owner?: Owner\n  workspace_icon: string\n  workspace_id: number\n  workspace_name: string\n}\n\n// Any config required that isn't part of the `OAuthUserConfig` spec should belong here\n// For example, we must pass a `redirectUri` to the Notion API when requesting tokens, therefore we add it here\ninterface AdditionalConfig {\n  redirectUri: string\n}\n\nconst NOTION_HOST = \"https://api.notion.com\"\nconst NOTION_API_VERSION = \"2022-06-28\"\n\n/**\n * Add Notion login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/notion\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Notion from \"@auth/core/providers/notion\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Notion({\n *       clientId: NOTION_CLIENT_ID,\n *       clientSecret: NOTION_CLIENT_SECRET,\n *       redirectUri: NOTION_CLIENT_REDIRECT_URI,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n * - [Notion Docs](https://developers.notion.com/docs)\n * - [Notion Authorization Docs](https://developers.notion.com/docs/authorization)\n * - [Notion Integrations](https://www.notion.so/my-integrations)\n *\n * ### Notes\n * You need to select \"Public Integration\" on the configuration page to get an `oauth_id` and `oauth_secret`. Private integrations do not provide these details.\n * You must provide a `clientId` and `clientSecret` to use this provider, as-well as a redirect URI (due to this being required by Notion endpoint to fetch tokens).\n *\n * :::tip\n *\n * The Notion provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/notion.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function NotionProvider<P extends NotionProfile>(\n  options: OAuthUserConfig<P> & AdditionalConfig\n): OAuthConfig<P> {\n  return {\n    id: \"notion\",\n    name: \"Notion\",\n    type: \"oauth\",\n    token: {\n      url: `${NOTION_HOST}/v1/oauth/token`,\n    },\n    userinfo: {\n      url: `${NOTION_HOST}/v1/users`,\n\n      // The result of this method will be the input to the `profile` callback.\n      // We use a custom request handler, since we need to do things such as pass the \"Notion-Version\" header\n      // More info: https://authjs.dev/getting-started/providers/notion\n      async request(context) {\n        const profile = await fetch(`${NOTION_HOST}/v1/users/me`, {\n          headers: {\n            Authorization: `Bearer ${context.tokens.access_token}`,\n            \"Notion-Version\": NOTION_API_VERSION,\n          },\n        })\n\n        const {\n          bot: {\n            owner: { user },\n          },\n        } = await profile.json()\n\n        return user\n      },\n    },\n    authorization: {\n      params: {\n        client_id: options.clientId,\n        response_type: \"code\",\n        owner: \"user\",\n        redirect_uri: options.redirectUri,\n      },\n      url: `${NOTION_HOST}/v1/oauth/authorize`,\n    },\n\n    async profile(profile) {\n      return {\n        id: profile.id,\n        name: profile.name,\n        email: profile.person.email,\n        image: profile.avatar_url,\n      }\n    },\n    style: { bg: \"#fff\", text: \"#000\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/oauth.ts",
    "content": "import type { Client, PrivateKey } from \"oauth4webapi\"\nimport type { CommonProviderOptions } from \"../providers/index.js\"\nimport type { Awaitable, Profile, TokenSet, User } from \"../types.js\"\nimport type { AuthConfig } from \"../index.js\"\nimport type { conformInternal, customFetch } from \"../lib/symbols.js\"\n\n// TODO: fix types\ntype AuthorizationParameters = any\ntype CallbackParamsType = any\ntype IssuerMetadata = any\ntype OAuthCallbackChecks = any\ntype OpenIDCallbackChecks = any\n\nexport type { OAuthProviderId } from \"./provider-types.js\"\n\nexport type OAuthChecks = OpenIDCallbackChecks | OAuthCallbackChecks\n\ntype PartialIssuer = Partial<Pick<IssuerMetadata, \"jwks_endpoint\" | \"issuer\">>\n\ntype UrlParams = Record<string, unknown>\n\ntype EndpointRequest<C, R, P> = (\n  context: C & {\n    /** Provider is passed for convenience, and also contains the `callbackUrl`. */\n    provider: OAuthConfigInternal<P> & {\n      signinUrl: string\n      callbackUrl: string\n    }\n  }\n) => Awaitable<R> | void\n\n/** Gives granular control of the request to the given endpoint */\ninterface AdvancedEndpointHandler<P extends UrlParams, C, R> {\n  /** Endpoint URL. Can contain parameters. Optionally, you can use `params` */\n  url?: string\n  /** These will be prepended to the `url` */\n  params?: P\n  /**\n   * Control the corresponding OAuth endpoint request completely.\n   * Useful if your provider relies on some custom behaviour\n   * or it diverges from the OAuth spec.\n   *\n   * - ⚠ **This is an advanced option.**\n   * You should **try to avoid using advanced options** unless you are very comfortable using them.\n   */\n  request?: EndpointRequest<C, R, P>\n  /** @internal */\n  conform?: (response: Response) => Awaitable<Response | undefined>\n  clientPrivateKey?: CryptoKey | PrivateKey\n}\n\n/**\n * Either an URL (containing all the parameters) or an object with more granular control.\n * @internal\n */\nexport type EndpointHandler<\n  P extends UrlParams,\n  C = any,\n  R = any,\n> = AdvancedEndpointHandler<P, C, R>\n\nexport type AuthorizationEndpointHandler =\n  EndpointHandler<AuthorizationParameters>\n\nexport type TokenEndpointHandler = EndpointHandler<\n  UrlParams,\n  {\n    /**\n     * Parameters extracted from the request to the `/api/auth/callback/:providerId` endpoint.\n     * Contains params like `state`.\n     */\n    params: CallbackParamsType\n    /**\n     * When using this custom flow, make sure to do all the necessary security checks.\n     * This object contains parameters you have to match against the request to make sure it is valid.\n     */\n    checks: OAuthChecks\n  },\n  {\n    tokens: TokenSet\n  }\n>\n\nexport type UserinfoEndpointHandler = EndpointHandler<\n  UrlParams,\n  { tokens: TokenSet },\n  Profile\n>\n\nexport type ProfileCallback<Profile> = (\n  profile: Profile,\n  tokens: TokenSet\n) => Awaitable<User>\n\nexport type AccountCallback = (tokens: TokenSet) => TokenSet | undefined | void\n\nexport interface OAuthProviderButtonStyles {\n  logo?: string\n  /**\n   * @deprecated\n   */\n  text?: string\n  /**\n   * @deprecated Please use 'brandColor' instead\n   */\n  bg?: string\n  brandColor?: string\n}\n\n/** TODO: Document */\nexport interface OAuth2Config<Profile>\n  extends CommonProviderOptions,\n    PartialIssuer {\n  /**\n   * Identifies the provider when you want to sign in to\n   * a specific provider.\n   *\n   * @example\n   * ```ts\n   * signIn('github') // \"github\" is the provider ID\n   * ```\n   */\n  id: string\n  /** The name of the provider. shown on the default sign in page. */\n  name: string\n  /**\n   * OpenID Connect (OIDC) compliant providers can configure\n   * this instead of `authorize`/`token`/`userinfo` options\n   * without further configuration needed in most cases.\n   * You can still use the `authorize`/`token`/`userinfo`\n   * options for advanced control.\n   *\n   * [Authorization Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414#section-3)\n   */\n  wellKnown?: string\n  issuer?: string\n  /**\n   * The login process will be initiated by sending the user to this URL.\n   *\n   * [Authorization endpoint](https://datatracker.ietf.org/doc/html/rfc6749#section-3.1)\n   */\n  authorization?: string | AuthorizationEndpointHandler\n  token?: string | TokenEndpointHandler\n  userinfo?: string | UserinfoEndpointHandler\n  type: \"oauth\"\n  /**\n   * Receives the full {@link Profile} returned by the OAuth provider, and returns a subset.\n   * It is used to create the user in the database.\n   *\n   * Defaults to: `id`, `email`, `name`, `image`\n   *\n   * @see [Database Adapter: User model](https://authjs.dev/reference/core/adapters#user)\n   */\n  profile?: ProfileCallback<Profile>\n  /**\n   * Receives the full {@link TokenSet} returned by the OAuth provider, and returns a subset.\n   * It is used to create the account associated with a user in the database.\n   *\n   * :::note\n   * You need to adjust your database's [Account model](https://authjs.dev/reference/core/adapters#account) to match the returned properties.\n   * Check out the documentation of your [database adapter](https://authjs.dev/reference/core/adapters) for more information.\n   * :::\n   *\n   * Defaults to: `access_token`, `id_token`, `refresh_token`, `expires_at`, `scope`, `token_type`, `session_state`\n   *\n   * @example\n   * ```ts\n   * import GitHub from \"@auth/core/providers/github\"\n   * // ...\n   * GitHub({\n   *   account(account) {\n   *     // https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/refreshing-user-access-tokens#refreshing-a-user-access-token-with-a-refresh-token\n   *     const refresh_token_expires_at =\n   *       Math.floor(Date.now() / 1000) + Number(account.refresh_token_expires_in)\n   *     return {\n   *       access_token: account.access_token,\n   *       expires_at: account.expires_at,\n   *       refresh_token: account.refresh_token,\n   *       refresh_token_expires_at\n   *     }\n   *   }\n   * })\n   * ```\n   *\n   * @see [Database Adapter: Account model](https://authjs.dev/reference/core/adapters#account)\n   * @see https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse\n   * @see https://www.ietf.org/rfc/rfc6749.html#section-5.1\n   */\n  account?: AccountCallback\n  /**\n   * The CSRF protection performed on the callback endpoint.\n   * @default [\"pkce\"]\n   *\n   * @note When `redirectProxyUrl` or {@link AuthConfig.redirectProxyUrl} is set,\n   * `\"state\"` will be added to checks automatically.\n   *\n   * [RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients (PKCE)](https://www.rfc-editor.org/rfc/rfc7636.html#section-4) |\n   * [RFC 6749 - The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1.1) |\n   * [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) |\n   */\n  checks?: Array<\"pkce\" | \"state\" | \"none\">\n  clientId?: string\n  clientSecret?: string\n  /**\n   * Pass overrides to the underlying OAuth library.\n   * See [`oauth4webapi` client](https://github.com/panva/oauth4webapi/blob/main/docs/interfaces/Client.md) for details.\n   */\n  client?: Partial<Client & { token_endpoint_auth_method: string }>\n  style?: OAuthProviderButtonStyles\n  /**\n   * Normally, when you sign in with an OAuth provider and another account\n   * with the same email address already exists,\n   * the accounts are not linked automatically.\n   *\n   * Automatic account linking on sign in is not secure\n   * between arbitrary providers and is disabled by default.\n   * Learn more in our [Security FAQ](https://authjs.dev/concepts#security).\n   *\n   * However, it may be desirable to allow automatic account linking if you trust that the provider involved has securely verified the email address\n   * associated with the account. Set `allowDangerousEmailAccountLinking: true`\n   * to enable automatic account linking.\n   */\n  allowDangerousEmailAccountLinking?: boolean\n  redirectProxyUrl?: AuthConfig[\"redirectProxyUrl\"]\n  /** @see {customFetch} */\n  [customFetch]?: typeof fetch\n  /**\n   * The options provided by the user.\n   * We will perform a deep-merge of these values\n   * with the default configuration.\n   *\n   * @internal\n   */\n  /** @see {conformInternal} */\n  [conformInternal]?: true\n  options?: OAuthUserConfig<Profile>\n}\n\n/**\n * Extension of the {@link OAuth2Config}.\n *\n * @see https://openid.net/specs/openid-connect-core-1_0.html\n */\nexport interface OIDCConfig<Profile>\n  extends Omit<OAuth2Config<Profile>, \"type\" | \"checks\"> {\n  type: \"oidc\"\n  checks?: Array<NonNullable<OAuth2Config<Profile>[\"checks\"]>[number] | \"nonce\">\n  /**\n   * If set to `false`, the `userinfo_endpoint` will be fetched for the user data.\n   * @note An `id_token` is still required to be returned during the authorization flow.\n   */\n  idToken?: boolean\n}\n\nexport type OAuthConfig<Profile> = OIDCConfig<Profile> | OAuth2Config<Profile>\n\nexport type OAuthEndpointType = \"authorization\" | \"token\" | \"userinfo\"\n\n/**\n * We parsed `authorization`, `token` and `userinfo`\n * to always contain a valid `URL`, with the params\n * @internal\n */\nexport type OAuthConfigInternal<Profile> = Omit<\n  OAuthConfig<Profile>,\n  OAuthEndpointType | \"redirectProxyUrl\"\n> & {\n  authorization?: { url: URL }\n  token?: {\n    url: URL\n    request?: TokenEndpointHandler[\"request\"]\n    clientPrivateKey?: CryptoKey | PrivateKey\n    /**\n     * @internal\n     * @deprecated\n     */\n    conform?: TokenEndpointHandler[\"conform\"]\n  }\n  userinfo?: { url: URL; request?: UserinfoEndpointHandler[\"request\"] }\n  /**\n   * Reconstructed from {@link OAuth2Config.redirectProxyUrl},\n   * adding the callback action and provider id onto the URL.\n   *\n   * If defined, it is favoured over {@link OAuthConfigInternal.callbackUrl} in the authorization request.\n   *\n   * When {@link InternalOptions.isOnRedirectProxy} is set, the actual value is saved in the decoded `state.origin` parameter.\n   *\n   * @example `\"https://auth.example.com/api/auth/callback/:provider\"`\n   *\n   */\n  redirectProxyUrl?: OAuth2Config<Profile>[\"redirectProxyUrl\"]\n} & Pick<\n    Required<OAuthConfig<Profile>>,\n    \"clientId\" | \"checks\" | \"profile\" | \"account\"\n  >\n\nexport type OIDCConfigInternal<Profile> = OAuthConfigInternal<Profile> & {\n  checks: OIDCConfig<Profile>[\"checks\"]\n  idToken: OIDCConfig<Profile>[\"idToken\"]\n}\n\nexport type OAuthUserConfig<Profile> = Omit<\n  Partial<OAuthConfig<Profile>>,\n  \"options\" | \"type\"\n>\n\nexport type OIDCUserConfig<Profile> = Omit<\n  Partial<OIDCConfig<Profile>>,\n  \"options\" | \"type\"\n>\n"
  },
  {
    "path": "packages/core/src/providers/okta.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Okta</b> integration.</span>\n * <a href=\"https://okta.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/okta.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/okta\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface OktaProfile extends Record<string, any> {\n  iss: string\n  ver: string\n  sub: string\n  aud: string\n  iat: string\n  exp: string\n  jti: string\n  auth_time: string\n  amr: string\n  idp: string\n  nonce: string\n  name: string\n  nickname: string\n  preferred_username: string\n  given_name: string\n  middle_name: string\n  family_name: string\n  email: string\n  email_verified: string\n  profile: string\n  zoneinfo: string\n  locale: string\n  address: string\n  phone_number: string\n  picture: string\n  website: string\n  gender: string\n  birthdate: string\n  updated_at: string\n  at_hash: string\n  c_hash: string\n}\n\n/**\n * Add Okta login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/okta\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Okta from \"@auth/core/providers/okta\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Okta({\n *       clientId: OKTA_CLIENT_ID,\n *       clientSecret: OKTA_CLIENT_SECRET,\n *       issuer: OKTA_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Okta OAuth documentation](https://developer.okta.com/docs/reference/api/oidc)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Okta provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * The Okta provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/okta.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Okta<P extends OktaProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"okta\",\n    name: \"Okta\",\n    type: \"oidc\",\n    style: { bg: \"#000\", text: \"#fff\" },\n    checks: [\"pkce\", \"state\"],\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/onelogin.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>OneLogin</b> integration.</span>\n * <a href=\"https://onelogin.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/onelogin.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/onelogin\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add OneLogin login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/onelogin\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import OneLogin from \"@auth/core/providers/onelogin\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     OneLogin({\n *       clientId: ONELOGIN_CLIENT_ID,\n *       clientSecret: ONELOGIN_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [OneLogin OAuth documentation](https://example.com)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the OneLogin provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * The OneLogin provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/onelogin.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function OneLogin(\n  config: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"onelogin\",\n    name: \"OneLogin\",\n    type: \"oidc\",\n    wellKnown: `${config.issuer}/oidc/2/.well-known/openid-configuration`,\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/ory-hydra.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Ory Hydra</b> integration.</span>\n * <a href=\"https://www.ory.sh/hydra/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/ory.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/ory-hydra\n */\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\nexport interface OryHydraProfile extends Record<string, any> {\n  iss: string\n  ver: string\n  sub: string\n  aud: string\n  iat: string\n  exp: string\n  jti: string\n  amr: string\n  email?: string\n}\n\n/**\n * Add Ory Hydra login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/hydra\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import OryHydra from \"@auth/core/providers/ory-hydra\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     OryHydra({\n *       clientId: ORY_HYDRA_CLIENT_ID,\n *       clientSecret: ORY_HYDRA_CLIENT_SECRET,\n *       issuer: ORY_HYDRA_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Ory Hydra documentation](https://www.ory.sh/docs/hydra/5min-tutorial)\n *\n * ### Notes\n *\n * Ory Hydra can be setup using the default Ory Network setup or self hosted on your own\n * infrastructure.\n * By default, Auth.js assumes that the Ory Hydra provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * The Ory Hydra provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/ory-hydra.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function OryHydra<P extends OryHydraProfile>(\n  options: OIDCUserConfig<P>\n): OIDCConfig<P> {\n  return {\n    id: \"hydra\",\n    name: \"Hydra\",\n    type: \"oidc\",\n    style: {\n      bg: \"#fff\",\n      text: \"#0F172A\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/osso.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Osso</b> integration.</span>\n * <a href=\"https://ossoapp.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/osso.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/osso\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add Osso login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/osso\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Osso from \"@auth/core/providers/osso\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Osso({\n *       clientId: OSSO_CLIENT_ID,\n *       clientSecret: OSSO_CLIENT_SECRET,\n *       issuer: OSSO_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n * Osso is an open source service that handles SAML authentication against Identity Providers, normalizes profiles, and makes those profiles available to you in an OAuth 2.0 code grant flow.\n * \n * - If you don't yet have an Osso instance, you can use [Osso's Demo App](https://demo.ossoapp.com) for your testing purposes. For documentation on deploying an Osso instance, see https://ossoapp.com/docs/deploy/overview/\n *  - [Osso OAuth documentation](https://ossoapp.com/)\n *\n * You can configure your OAuth Clients on your Osso Admin UI, i.e. https://demo.ossoapp.com/admin/config - you'll need to get a Client ID and Secret and allow-list your redirect URIs.\n * [SAML SSO differs a bit from OAuth](https://ossoapp.com/blog/saml-vs-oauth) - for every tenant who wants to sign in to your application using SAML, you and your customer need to perform a multi-step configuration in Osso's Admin UI and the admin dashboard of the tenant's Identity Provider. Osso provides documentation for providers like Okta and OneLogin, cloud-based IDPs who also offer a developer account that's useful for testing. Osso also provides a [Mock IDP](https://idp.ossoapp.com) that you can use for testing without needing to sign up for an Identity Provider service.\n\n * See Osso's complete configuration and testing documentation at https://ossoapp.com/docs/configure/overview\n * \n * ### Notes\n *\n * By default, Auth.js assumes that the Osso provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::note\n * \n * `issuer` should be the fully qualified domain e.g. `demo.ossoapp.com`\n * \n * :::\n * \n * :::tip\n *\n * The Osso provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/osso.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Osso(\n  config: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"osso\",\n    name: \"Osso\",\n    type: \"oauth\",\n    authorization: `${config.issuer}oauth/authorize`,\n    token: `${config.issuer}oauth/token`,\n    userinfo: `${config.issuer}oauth/me`,\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/osu.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>osu!</b> integration.</span>\n * <a href=\"https://osu.ppy.sh/home\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/osu.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/osu\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface OsuUserCompact {\n  avatar_url: string\n  country_code: string\n  default_group: string\n  id: number\n  is_active: boolean\n  is_bot: boolean\n  is_deleted: boolean\n  is_online: boolean\n  is_supporter: boolean\n  last_visit: Date | null\n  pm_friends_only: boolean\n  profile_colour: string | null\n  username: string\n}\n\nexport interface OsuProfile extends OsuUserCompact, Record<string, any> {\n  discord: string | null\n  has_supported: boolean\n  interests: string | null\n  join_date: Date\n  kudosu: {\n    available: number\n    total: number\n  }\n  location: string | null\n  max_blocks: number\n  max_friends: number\n  occupation: string | null\n  playmode: string\n  playstyle: string[]\n  post_count: number\n  profile_order: string[]\n  title: string | null\n  title_url: string | null\n  twitter: string | null\n  website: string | null\n  country: {\n    code: string\n    name: string\n  }\n  cover: {\n    custom_url: string | null\n    url: string\n    id: number | null\n  }\n  is_restricted: boolean\n}\n\n/**\n * Add osu! login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/osu\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Osu from \"@auth/core/providers/osu\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Osu({ clientId: OSU_CLIENT_ID, clientSecret: OSU_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [osu! OAuth documentation](https://osu.ppy.sh/docs/index.html#authentication)\n *  - [osu! app console](https://osu.ppy.sh/home/account/edit#new-oauth-application)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Osu provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::note\n *\n * osu! does not provide a user email.\n *\n * :::\n *\n * :::tip\n *\n * The osu! provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/osu.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Osu<P extends OsuProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"osu\",\n    name: \"osu!\",\n    type: \"oauth\",\n    token: \"https://osu.ppy.sh/oauth/token\",\n    authorization: \"https://osu.ppy.sh/oauth/authorize?scope=identify\",\n    userinfo: \"https://osu.ppy.sh/api/v2/me\",\n    profile(profile) {\n      return {\n        id: profile.id.toString(),\n        email: null,\n        name: profile.username,\n        image: profile.avatar_url,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/passage.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Passage by 1Password</b> integration.</span>\n * <a href=\"https://passage.1password.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/passage.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/passage\n */\n\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/** @see [Supported Scopes](https://docs.passage.id/hosted-login/oidc-client-configuration#supported-scopes) */\nexport interface PassageProfile {\n  iss: string\n  /** Unique identifer in Passage for the user */\n  sub: string\n  aud: string[]\n  exp: number\n  iat: number\n  auth_time: number\n  azp: string\n  client_id: string\n  at_hash: string\n  c_hash: string\n  /** The user's email address */\n  email: string\n  /** Whether the user has verified their email address */\n  email_verified: boolean\n  /** The user's phone number */\n  phone: string\n  /** Whether the user has verified their phone number */\n  phone_number_verified: boolean\n}\n\n/**\n * Add Passage login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/passage\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Passage from \"@auth/core/providers/passage\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Passage({\n *       clientId: PASSAGE_ID,\n *       clientSecret: PASSAGE_SECRET,\n *       issuer: PASSAGE_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Passage OIDC documentation](https://docs.passage.id/hosted-login/oidc-client-configuration)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Passage provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * The Passage provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/passage.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Passage(\n  config: OAuthUserConfig<PassageProfile>\n): OAuthConfig<PassageProfile> {\n  return {\n    id: \"passage\",\n    name: \"Passage\",\n    type: \"oidc\",\n    client: { token_endpoint_auth_method: \"client_secret_basic\" },\n    style: {\n      brandColor: \"#3d53f6\",\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/passkey.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#24292f\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Passkey</b> integration.</span>\n * <a href=\"https://passkeys.dev\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/passkey.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/passkey\n */\n\nimport WebAuthn, {\n  WebAuthnConfig,\n  DEFAULT_WEBAUTHN_TIMEOUT,\n} from \"./webauthn.js\"\n\n/**\n * Add Passkey login to your page.\n *\n * ### Setup\n *\n * Install the required peer dependency.\n *\n * ```bash npm2yarn\n * npm install @simplewebauthn/browser@9.0.1\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Passkey from \"@auth/core/providers/passkey\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [Passkey],\n * })\n * ```\n * ### Resources\n *\n * - [SimpleWebAuthn - Server side](https://simplewebauthn.dev/docs/packages/server)\n * - [SimpleWebAuthn - Client side](https://simplewebauthn.dev/docs/packages/client)\n * - [Passkeys.dev - Intro](https://passkeys.dev/docs/intro/what-are-passkeys/)\n * - [Passkeys.dev - Specifications](https://passkeys.dev/docs/reference/specs/)\n * - [Source code](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/passkey.ts)\n *\n * ### Notes\n *\n * This provider is an extension of the WebAuthn provider that defines some default values\n * associated with Passkey support. You may override these, but be aware that authenticators\n * may not recognize your credentials as Passkey credentials if you do.\n *\n * :::tip\n *\n * The Passkey provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/passkey.ts).\n * To override the defaults for your use case, check out [customizing a built-in WebAuthn provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Passkey(\n  config: Partial<WebAuthnConfig>\n): WebAuthnConfig {\n  return WebAuthn({\n    id: \"passkey\",\n    name: \"Passkey\",\n    authenticationOptions: {\n      timeout: DEFAULT_WEBAUTHN_TIMEOUT,\n      userVerification: \"required\",\n    },\n    registrationOptions: {\n      timeout: DEFAULT_WEBAUTHN_TIMEOUT,\n      authenticatorSelection: {\n        residentKey: \"required\",\n        userVerification: \"required\",\n      },\n    },\n    verifyAuthenticationOptions: {\n      requireUserVerification: true,\n    },\n    verifyRegistrationOptions: {\n      requireUserVerification: true,\n    },\n    ...config,\n  })\n}\n"
  },
  {
    "path": "packages/core/src/providers/patreon.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Patreon</b> integration.</span>\n * <a href=\"https://www.patreon.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/patreon.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/patreon\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface PatreonProfile extends Record<string, any> {\n  sub: string\n  nickname: string\n  email: string\n  picture: string\n}\n\n/**\n * Add Patreon login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/patreon\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Patreon from \"@auth/core/providers/patreon\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Patreon({\n *       clientId: PATREON_CLIENT_ID,\n *       clientSecret: PATREON_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Patreon OAuth documentation](https://docs.patreon.com/#apiv2-oauth)\n *  - [Patreon Platform](https://www.patreon.com/portal/registration/register-clients)\n *  - [ApiV2 Scopes](https://docs.patreon.com/#scopes)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Patreon provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Patreon provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/patreon.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Patreon<P extends PatreonProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"patreon\",\n    name: \"Patreon\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://www.patreon.com/oauth2/authorize\",\n      params: { scope: \"identity identity[email]\" },\n    },\n    token: \"https://www.patreon.com/api/oauth2/token\",\n    userinfo: \"https://www.patreon.com/api/oauth2/api/current_user\",\n    profile(profile) {\n      return {\n        id: profile.data.id,\n        name: profile.data.attributes.full_name,\n        email: profile.data.attributes.email,\n        image: profile.data.attributes.image_url,\n      }\n    },\n    style: { bg: \"#e85b46\", text: \"#fff\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/ping-id.ts",
    "content": "import type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\nexport interface PingProfile extends Record<string, any> {\n  iss: string\n  sub: string\n  aud: string\n  iat: number\n  exp: number\n  acr: string\n  amr: [string]\n  auth_time: number\n  at_hash: string\n  sid: string\n  preferred_username: string\n  given_name: string\n  picture: string\n  updated_at: number\n  name: string\n  family_name: string\n  email: string\n  env: string\n  org: string\n  \"p1.region\": string\n}\n\n/**\n * Add PingId login to your page.\n *\n * ## Documentation\n *\n * - [Create App in Ping Identity](https://docs.pingidentity.com/r/en-us/pingone/p1_add_app_worker)\n *\n *  ---\n * ## Example\n *\n * ```ts\n * import PingId from \"@auth/core/providers/ping-id\"\n *\n * ...\n * providers: [\n *  PingId({\n *    clientId: AUTH_PING_ID_ID,\n *    clientSecret: AUTH_PING_ID_SECRET,\n *    issuer: PING_ID_ISSUER\n *  })\n * ]\n * ...\n * ```\n *\n * ## Help\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n */\n\nexport default function PingId(\n  options: OIDCUserConfig<PingProfile>\n): OIDCConfig<PingProfile> {\n  return {\n    id: \"ping-id\",\n    name: \"Ping Identity\",\n    type: \"oidc\",\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/pinterest.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Pinterest</b> integration.</span>\n * <a href=\"https://www.pinterest.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/pinterest.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/pinterest\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface PinterestProfile extends Record<string, any> {\n  account_type: \"BUSINESS\" | \"PINNER\"\n  profile_image: string\n  website_url: string\n  username: string\n}\n\n/**\n * Add Pinterest login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/pinterest\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Pinterest from \"@auth/core/providers/pinterest\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Pinterest({\n *       clientId: PINTEREST_CLIENT_ID,\n *       clientSecret: PINTEREST_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Pinterest OAuth documentation](https://developers.pinterest.com/docs/getting-started/authentication/)\n *  - [Pinterest app console](https://developers.pinterest.com/apps/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Pinterest provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n *\n * :::tip\n *\n * To use in production, make sure the app has standard API access and not trial access\n *\n * :::\n *\n * :::tip\n *\n * The Pinterest provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/pinterest.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function PinterestProvider<P extends PinterestProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"pinterest\",\n    name: \"Pinterest\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://www.pinterest.com/oauth\",\n      params: { scope: \"user_accounts:read\" },\n    },\n    token: \"https://api.pinterest.com/v5/oauth/token\",\n    userinfo: \"https://api.pinterest.com/v5/user_account\",\n    profile({ username, profile_image }) {\n      return {\n        id: username,\n        name: username,\n        image: profile_image,\n        email: null,\n      }\n    },\n    style: {\n      brandColor: \"#bd081c\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/pipedrive.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Pipedrive</b> integration.</span>\n * <a href=\"https://www.pipedrive.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/pipedrive.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/pipedrive\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface PipedriveProfile extends Record<string, any> {\n  success: boolean\n  data: {\n    id: number\n    name: string\n    default_currency?: string\n    locale?: string\n    lang?: number\n    email: string\n    phone?: string\n    activated?: boolean\n    last_login?: Date\n    created?: Date\n    modified?: Date\n    signup_flow_variation?: string\n    has_created_company?: boolean\n    is_admin?: number\n    active_flag?: boolean\n    timezone_name?: string\n    timezone_offset?: string\n    role_id?: number\n    icon_url?: string\n    is_you?: boolean\n    company_id?: number\n    company_name?: string\n    company_domain?: string\n    company_country?: string\n    company_industry?: string\n    language?: {\n      language_code?: string\n      country_code?: string\n    }\n  }\n}\n\n/**\n * Add Pipedrive login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/pipedrive\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Pipedrive from \"@auth/core/providers/pipedrive\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Pipedrive({\n *       clientId: PIPEDRIVE_CLIENT_ID,\n *       clientSecret: PIPEDRIVE_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Pipedrive OAuth documentation](https://pipedrive.readme.io/docs/marketplace-oauth-authorization)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Pipedrive provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Pipedrive provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/pipedrive.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Pipedrive<P extends PipedriveProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"pipedrive\",\n    name: \"Pipedrive\",\n    type: \"oauth\",\n    authorization: \"https://oauth.pipedrive.com/oauth/authorize\",\n    token: \"https://oauth.pipedrive.com/oauth/token\",\n    userinfo: \"https://api.pipedrive.com/users/me\",\n    profile: ({ data: profile }) => {\n      return {\n        id: profile.id.toString(),\n        name: profile.name,\n        email: profile.email,\n        image: profile.icon_url,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/postmark.ts",
    "content": "import type { EmailConfig, EmailUserConfig } from \"./index.js\"\nimport { html, text } from \"../lib/utils/email.js\"\n\n/** @todo Document this */\nexport default function Postmark(config: EmailUserConfig): EmailConfig {\n  return {\n    id: \"postmark\",\n    type: \"email\",\n    name: \"Postmark\",\n    from: \"Auth.js <no-reply@authjs.dev>\",\n    maxAge: 24 * 60 * 60,\n    async sendVerificationRequest(params) {\n      const { identifier: to, provider, url, theme } = params\n      const { host } = new URL(url)\n      if (!provider.apiKey) throw new TypeError(\"Missing Postmark API Key\")\n      const res = await fetch(\"https://api.postmarkapp.com/email\", {\n        method: \"POST\",\n        headers: {\n          Accept: \"application/json\",\n          \"Content-Type\": \"application/json\",\n          \"X-Postmark-Server-Token\": provider.apiKey,\n        },\n        body: JSON.stringify({\n          From: provider.from,\n          To: to,\n          Subject: `Sign in to ${host}`,\n          TextBody: text({ url, host }),\n          HtmlBody: html({ url, host, theme }),\n          MessageStream: \"outbound\",\n        }),\n      })\n\n      if (!res.ok)\n        throw new Error(\"Postmark error: \" + JSON.stringify(await res.json()))\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/reddit.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Reddit</b> integration.</span>\n * <a href=\"https://www.reddit.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/reddit.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/reddit\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add Reddit login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/reddit\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Reddit from \"@auth/core/providers/reddit\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Reddit({ clientId: REDDIT_CLIENT_ID, clientSecret: REDDIT_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Reddit API documentation](https://www.reddit.com/dev/api/)\n * - [Reddit app console](https://www.reddit.com/prefs/apps/ )\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Reddit provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::danger\n *\n * Reddit requires authorization every time you go through their page.\n * Only allows one callback URL per Client ID / Client Secret.\n *\n * :::\n *\n * :::tip\n *\n * This Provider template only has a one hour access token to it and only has the \"identity\" scope. If you want to get a refresh token as well you must follow this:\n *```ts\n * providers: [\n *  Reddit({\n *    clientId: process.env.REDDIT_CLIENT_ID,\n *    clientSecret: process.env.REDDIT_CLIENT_SECRET,\n *    authorization: {\n *      params: {\n *        duration: 'permanent',\n *      },\n *    },\n *  }),\n * ]\n * ```\n * :::\n *\n * :::tip\n *\n * The Reddit provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/reddit.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Reddit(\n  config: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"reddit\",\n    name: \"Reddit\",\n    type: \"oauth\",\n    authorization: \"https://www.reddit.com/api/v1/authorize?scope=identity\",\n    token: \"https://www.reddit.com/api/v1/access_token\",\n    userinfo: \"https://oauth.reddit.com/api/v1/me\",\n    checks: [\"state\"],\n    style: {\n      brandColor: \"#FF4500\",\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/resend.ts",
    "content": "import type { EmailConfig, EmailUserConfig } from \"./index.js\"\nimport { html, text } from \"../lib/utils/email.js\"\n\n/** @todo Document this */\nexport default function Resend(config: EmailUserConfig): EmailConfig {\n  return {\n    id: \"resend\",\n    type: \"email\",\n    name: \"Resend\",\n    from: \"Auth.js <no-reply@authjs.dev>\",\n    maxAge: 24 * 60 * 60,\n    async sendVerificationRequest(params) {\n      const { identifier: to, provider, url, theme } = params\n      const { host } = new URL(url)\n      const res = await fetch(\"https://api.resend.com/emails\", {\n        method: \"POST\",\n        headers: {\n          Authorization: `Bearer ${provider.apiKey}`,\n          \"Content-Type\": \"application/json\",\n        },\n        body: JSON.stringify({\n          from: provider.from,\n          to,\n          subject: `Sign in to ${host}`,\n          html: html({ url, host, theme }),\n          text: text({ url, host }),\n        }),\n      })\n\n      if (!res.ok)\n        throw new Error(\"Resend error: \" + JSON.stringify(await res.json()))\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/roblox.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Roblox</b> integration.</span>\n * <a href=\"https://roblox.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/roblox.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/roblox\n */\nimport type { OIDCUserConfig, OIDCConfig } from \"./index.js\"\n\n/**\n * Corresponds to the user structure documented here:\n * https://create.roblox.com/docs/cloud/reference/oauth2 (Example User with Profile Scope)\n */\nexport interface RobloxProfile extends Record<string, any> {\n  /* Roblox user id */\n  sub: string\n\n  /* Roblox display name */\n  name: string\n\n  /* Roblox display name */\n  nickname: string\n\n  /* Roblox username */\n  preferred_username: string\n\n  /* Creation time of the Roblox account as a Unix timestamp. */\n  created_at: number\n\n  /* Roblox account profile URL */\n  profile: string\n\n  /* Roblox avatar headshot image. Can be null if the avatar headshot image hasn't yet been generated or has been moderated */\n  picture: string | null\n}\n\n/**\n * Add Roblox login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/roblox\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Roblox from \"@auth/providers/roblox\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Roblox({\n *       clientId: AUTH_ROBLOX_ID,\n *       clientSecret: AUTH_ROBLOX_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Roblox OAuth documentation](https://create.roblox.com/docs/cloud/open-cloud/oauth2-overview)\n *  - [Roblox OAuth apps](https://create.roblox.com/dashboard/credentials?activeTab=OAuthTab)\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Roblox(\n  options: OIDCUserConfig<RobloxProfile>\n): OIDCConfig<RobloxProfile> {\n  return {\n    id: \"roblox\",\n    name: \"Roblox\",\n    type: \"oidc\",\n    authorization: { params: { scope: \"openid profile\" } },\n    issuer: \"https://apis.roblox.com/oauth/\",\n    checks: [\"pkce\", \"state\"],\n    style: { bg: \"#5865F2\", text: \"#fff\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/salesforce.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#00a1e0\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Salesforce</b> integration.</span>\n * <a href=\"https://www.salesforce.com/ap/?ir=1\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/salesforce.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/salesforce\n */\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\nexport interface SalesforceProfile extends Record<string, any> {\n  sub: string\n  nickname: string\n  email: string\n  picture: string\n}\n\n/**\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/salesforce\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Salesforce from \"@auth/core/providers/salesforce\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Salesforce({\n *       clientId: AUTH_SALESFORCE_ID,\n *       clientSecret: AUTH_SALESFORCE_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Auth0 docs](https://auth0.com/docs/authenticate)\n *\n * ### Notes\n *\n * The Salesforce provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/salesforce.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * ## Help\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n */\nexport default function Salesforce(\n  options: OIDCUserConfig<SalesforceProfile>\n): OIDCConfig<SalesforceProfile> {\n  return {\n    id: \"salesforce\",\n    name: \"Salesforce\",\n    type: \"oidc\",\n    issuer: \"https://login.salesforce.com\",\n    idToken: false,\n    checks: [\"pkce\", \"state\", \"nonce\"],\n    style: { bg: \"#00a1e0\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/sendgrid.ts",
    "content": "import type { EmailConfig, EmailUserConfig } from \"./index.js\"\nimport { html, text } from \"../lib/utils/email.js\"\n\n/** @todo Document this */\nexport default function SendGrid(config: EmailUserConfig): EmailConfig {\n  return {\n    id: \"sendgrid\",\n    type: \"email\",\n    name: \"SendGrid\",\n    from: \"Auth.js <no-reply@authjs.dev>\",\n    maxAge: 24 * 60 * 60,\n    async sendVerificationRequest(params) {\n      const { identifier: to, provider, url, theme } = params\n      const { host } = new URL(url)\n      const res = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n        method: \"POST\",\n        headers: {\n          Authorization: `Bearer ${provider.apiKey}`,\n          \"Content-Type\": \"application/json\",\n        },\n        body: JSON.stringify({\n          personalizations: [{ to: [{ email: to }] }],\n          from: { email: provider.from },\n          subject: `Sign in to ${host}`,\n          content: [\n            { type: \"text/plain\", value: text({ url, host }) },\n            { type: \"text/html\", value: html({ url, host, theme }) },\n          ],\n        }),\n      })\n      // REVIEW: Clean up error handling\n      if (!res.ok) throw new Error(\"Sendgrid error: \" + (await res.text()))\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/simplelogin.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>SimpleLogin</b> integration.</span>\n * <a href=\"https://simplelogin.io\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/simplelogin.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/simplelogin\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface SimpleLoginProfile {\n  id: number\n  sub: string\n  email: string\n  email_verified: boolean\n  name: string\n  avatar_url: string | undefined\n  client: string\n}\n\n/**\n * Add SimpleLogin login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/simplelogin\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import SimpleLogin from \"@auth/core/providers/simplelogin\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     SimpleLogin({\n *       clientId: SIMPLELOGIN_CLIENT_ID,\n *       clientSecret: SIMPLELOGIN_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n *  - [Sign in with SimpleLogin](https://simplelogin.io/developer/)\n *  - [SimpleLogin OAuth documentation](https://simplelogin.io/docs/siwsl/intro/)\n *  - [SimpleLogin OAuth Configuration](https://app.simplelogin.io/developer)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the SimpleLogin provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * The \"Authorized redirect URIs\" used must include your full domain and end in the callback path. By default, SimpleLogin whitelists all `http[s]://localhost:*` address to facilitate local development. For example;\n *\n * - For production: `https://{YOUR_DOMAIN}/api/auth/callback/simplelogin`\n * - For development: By default **localhost** is whitelisted.\n *\n * :::warning\n *\n * **Authorized Redirect URIs** must be **HTTPS** for security reason (except for `localhost`).\n *\n * :::\n *\n * :::tip\n *\n * The SimpleLogin provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/simplelogin.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function SimpleLogin<P extends SimpleLoginProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"simplelogin\",\n    name: \"SimpleLogin\",\n    type: \"oidc\",\n    issuer: \"https://app.simplelogin.io\",\n    profile(profile) {\n      return {\n        id: profile.sub,\n        name: profile.name,\n        email: profile.email,\n        image: profile.avatar_url,\n      }\n    },\n    style: { brandColor: \"#e3156a\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/slack.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Slack</b> integration.</span>\n * <a href=\"https://www.slack.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/slack.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/slack\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface SlackProfile extends Record<string, any> {\n  ok: boolean\n  sub: string\n  \"https://slack.com/user_id\": string\n  \"https://slack.com/team_id\": string\n  email: string\n  email_verified: boolean\n  date_email_verified: number\n  name: string\n  picture: string\n  given_name: string\n  family_name: string\n  locale: string\n  \"https://slack.com/team_name\": string\n  \"https://slack.com/team_domain\": string\n  \"https://slack.com/user_image_24\": string\n  \"https://slack.com/user_image_32\": string\n  \"https://slack.com/user_image_48\": string\n  \"https://slack.com/user_image_72\": string\n  \"https://slack.com/user_image_192\": string\n  \"https://slack.com/user_image_512\": string\n  \"https://slack.com/user_image_1024\": string\n  \"https://slack.com/team_image_34\": string\n  \"https://slack.com/team_image_44\": string\n  \"https://slack.com/team_image_68\": string\n  \"https://slack.com/team_image_88\": string\n  \"https://slack.com/team_image_102\": string\n  \"https://slack.com/team_image_132\": string\n  \"https://slack.com/team_image_230\": string\n  \"https://slack.com/team_image_default\": boolean\n}\n\n/**\n * Add Slack login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/slack\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Slack from \"@auth/core/providers/slack\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Slack({ clientId: SLACK_CLIENT_ID, clientSecret: SLACK_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Slack Authentication documentation](https://api.slack.com/authentication)\n * - [Sign-in with Slack](https://api.slack.com/docs/sign-in-with-slack)\n * - [Slack app console](https://api.slack.com/apps)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Slack provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::danger\n *\n * Slack requires that the redirect URL of your app uses https, even for local development.\n * An easy workaround for this is using a service like [ngrok](https://ngrok.com/) that creates a secure tunnel to your app, using https. Remember to set the url as `NEXTAUTH_URL` as well.\n *\n * :::\n *\n * :::tip\n *\n * The Slack provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/slack.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Slack<P extends SlackProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"slack\",\n    name: \"Slack\",\n    type: \"oidc\",\n    issuer: \"https://slack.com\",\n    checks: [\"nonce\"],\n    style: { brandColor: \"#611f69\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/spotify.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Spotify</b> integration.</span>\n * <a href=\"https://www.spotify.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/spotify.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/spotify\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface SpotifyImage {\n  url: string\n}\n\nexport interface SpotifyProfile extends Record<string, any> {\n  id: string\n  display_name: string\n  email: string\n  images: SpotifyImage[]\n}\n\n/**\n * Add Spotify login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/spotify\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Spotify from \"@auth/core/providers/spotify\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Spotify({\n *       clientId: SPOTIFY_CLIENT_ID,\n *       clientSecret: SPOTIFY_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Spotify OAuth documentation](https://developer.spotify.com/documentation/general/guides/authorization-guide)\n * - [Spotify app console](https://developer.spotify.com/dashboard/applications)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Spotify provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Spotify provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/spotify.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Spotify<P extends SpotifyProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"spotify\",\n    name: \"Spotify\",\n    type: \"oauth\",\n    authorization:\n      \"https://accounts.spotify.com/authorize?scope=user-read-email\",\n    token: \"https://accounts.spotify.com/api/token\",\n    userinfo: \"https://api.spotify.com/v1/me\",\n    profile(profile) {\n      return {\n        id: profile.id,\n        name: profile.display_name,\n        email: profile.email,\n        image: profile.images?.[0]?.url,\n      }\n    },\n    style: { brandColor: \"#1db954\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/strava.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Strava</b> integration.</span>\n * <a href=\"https://www.strava.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/strava.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/strava\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface StravaProfile extends Record<string, any> {\n  id: string // this is really a number\n  firstname: string\n  lastname: string\n  profile: string\n}\n\n/**\n * Add Strava login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/strava\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Strava from \"@auth/core/providers/strava\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Strava({ clientId: STRAVA_CLIENT_ID, clientSecret: STRAVA_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Strava API documentation](http://developers.strava.com/docs/reference/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Strava provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Strava provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/strava.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Strava<P extends StravaProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"strava\",\n    name: \"Strava\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://www.strava.com/api/v3/oauth/authorize\",\n      params: {\n        scope: \"read\",\n        approval_prompt: \"auto\",\n        response_type: \"code\",\n      },\n    },\n    token: {\n      url: \"https://www.strava.com/api/v3/oauth/token\",\n    },\n    userinfo: \"https://www.strava.com/api/v3/athlete\",\n    client: {\n      token_endpoint_auth_method: \"client_secret_post\",\n    },\n    profile(profile) {\n      return {\n        id: profile.id,\n        name: `${profile.firstname} ${profile.lastname}`,\n        email: null,\n        image: profile.profile,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/threads.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Threads</b> integration.</span>\n * <a href=\"https://www.threads.net/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/threads.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/threads\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * [User](https://developers.facebook.com/docs/threads/reference/user)\n */\nexport interface ThreadsProfile {\n  data: {\n    /**\n     * Unique identifier of this user. This is returned as a string in order to avoid complications with languages and tools\n     * that cannot handle large integers.\n     */\n    id: string\n    /**\n     * The Threads handle (username) of this user.\n     *\n     * To return this field, add `fields=username` in the authorization request's query parameter.\n     */\n    username?: string\n    /**\n     * The URL to the profile image for this user, as shown on the user's profile.\n     *\n     * To return this field, add `fields=threads_profile_picture_url` in the authorization request's query parameter.\n     */\n    threads_profile_picture_url?: string\n    /**\n     * The text of this user's profile biography (also known as bio), if the user provided one.\n     *\n     * To return this field, add `fields=threads_biography` in the authorization request's query parameter.\n     */\n    threads_biography?: string\n  }\n}\n\n/**\n * Add Threads login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/threads\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Threads from \"@auth/core/providers/threads\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Threads({\n *       clientId: THREADS_CLIENT_ID,\n *       clientSecret: THREADS_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Threads OAuth documentation](https://developers.facebook.com/docs/threads)\n * - [Threads OAuth apps](https://developers.facebook.com/apps/)\n *\n * ### Notes\n *\n * :::warning\n *\n * Email address is not returned by the Threads API.\n *\n * :::\n *\n * :::tip\n *\n * Threads required callback URL to be configured in your Facebook app and Facebook required you to use **https** even for localhost! In order to do that, you either need to [add an SSL to your localhost](https://www.freecodecamp.org/news/how-to-get-https-working-on-your-local-development-environment-in-5-minutes-7af615770eec/) or use a proxy such as [ngrok](https://ngrok.com/docs).\n *\n * :::\n *\n * By default, Auth.js assumes that the Threads provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Threads provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/threads.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Threads(\n  config: OAuthUserConfig<ThreadsProfile>\n): OAuthConfig<ThreadsProfile> {\n  return {\n    id: \"threads\",\n    name: \"Threads\",\n    type: \"oauth\",\n    checks: [\"state\"],\n    authorization: \"https://threads.net/oauth/authorize?scope=threads_basic\",\n    token: \"https://graph.threads.net/oauth/access_token\",\n    userinfo:\n      \"https://graph.threads.net/v1.0/me?fields=id,username,threads_profile_picture_url\",\n    client: {\n      token_endpoint_auth_method: \"client_secret_post\",\n    },\n    profile({ data }) {\n      return {\n        id: data.id,\n        name: data.username || null,\n        email: null,\n        image: data.threads_profile_picture_url || null,\n      }\n    },\n    style: { bg: \"#000\", text: \"#fff\" },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/tiktok.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>TikTok</b> integration.</span>\n * <a href=\"https://www.tiktok.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/tiktok.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/tiktok\n */\nimport { customFetch } from \"../lib/symbols.js\"\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * [More info](https://developers.tiktok.com/doc/tiktok-api-v2-get-user-info/)\n */\nexport interface TiktokProfile {\n  data: {\n    user: {\n      /**\n       * The unique identification of the user in the current application.Open id\n       * for the client.\n       *\n       * To return this field, add `fields=open_id` in the user profile request's query parameter.\n       */\n      open_id: string\n      /**\n       * The unique identification of the user across different apps for the same developer.\n       * For example, if a partner has X number of clients,\n       * it will get X number of open_id for the same TikTok user,\n       * but one persistent union_id for the particular user.\n       *\n       * To return this field, add `fields=union_id` in the user profile request's query parameter.\n       */\n      union_id?: string\n      /**\n       * User's profile image.\n       *\n       * To return this field, add `fields=avatar_url` in the user profile request's query parameter.\n       */\n      avatar_url: string\n      /**\n       * User`s profile image in 100x100 size.\n       *\n       * To return this field, add `fields=avatar_url_100` in the user profile request's query parameter.\n       */\n      avatar_url_100?: string\n      /**\n       * User's profile image with higher resolution\n       *\n       * To return this field, add `fields=avatar_url_100` in the user profile request's query parameter.\n       */\n      avatar_large_url?: string\n      /**\n       * User's profile name\n       *\n       * To return this field, add `fields=display_name` in the user profile request's query parameter.\n       */\n      display_name: string\n      /**\n       * User's username.\n       *\n       * To return this field, add `fields=username` in the user profile request's query parameter.\n       */\n      username: string\n      /** @note Email is currently unsupported by TikTok  */\n      email?: string\n      /**\n       * User's bio description if there is a valid one.\n       *\n       * To return this field, add `fields=bio_description` in the user profile request's query parameter.\n       */\n      bio_description?: string\n      /**\n       * The link to user's TikTok profile page.\n       *\n       * To return this field, add `fields=profile_deep_link` in the user profile request's query parameter.\n       */\n      profile_deep_link?: string\n      /**\n       * Whether TikTok has provided a verified badge to the account after confirming\n       * that it belongs to the user it represents.\n       *\n       * To return this field, add `fields=is_verified` in the user profile request's query parameter.\n       */\n      is_verified?: boolean\n      /**\n       * User's followers count.\n       *\n       * To return this field, add `fields=follower_count` in the user profile request's query parameter.\n       */\n      follower_count?: number\n      /**\n       * The number of accounts that the user is following.\n       *\n       * To return this field, add `fields=following_count` in the user profile request's query parameter.\n       */\n      following_count?: number\n      /**\n       * The total number of likes received by the user across all of their videos.\n       *\n       * To return this field, add `fields=likes_count` in the user profile request's query parameter.\n       */\n      likes_count?: number\n      /**\n       * The total number of publicly posted videos by the user.\n       *\n       * To return this field, add `fields=video_count` in the user profile request's query parameter.\n       */\n      video_count?: number\n    }\n  }\n  error: {\n    /**\n     * The error category in string.\n     */\n    code: string\n    /**\n     * The error message in string.\n     */\n    message: string\n    /**\n     * The error message in string.\n     */\n    log_id: string\n  }\n}\n\n/**\n * Add TikTok login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/tiktok\n * ```\n *\n * #### Configuration\n * You can omit the client and secret if you have set the `AUTH_TIKTOK_ID` and `AUTH_TIKTOK_SECRET` environment variables.\n * Remeber that the AUTH_TIKTOK_ID is the Client Key in the TikTok Application\n *```ts\n * import { Auth } from \"@auth/core\"\n * import TikTok from \"@auth/core/providers/tiktok\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     TikTok({ clientId: AUTH_TIKTOK_ID, clientSecret: AUTH_TIKTOK_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *  - [TikTok app console](https://developers.tiktok.com/)\n *  - [TikTok login kit documentation](https://developers.tiktok.com/doc/login-kit-web/)\n *  - [Available Scopes](https://developers.tiktok.com/doc/tiktok-api-scopes/)\n *  - [Sandbox for testing](https://developers.tiktok.com/blog/introducing-sandbox)\n *\n *\n * ### Notes\n *\n * :::tip\n *\n * Production applications cannot use localhost URLs to sign in with TikTok. You need add the domain and Callback/Redirect url's to your TikTok app and have them review and approved by the TikTok Team.\n * If you need to test your implementation, you can use the sandbox feature and ngrok for testing in localhost.\n *\n * :::\n *\n * :::tip\n *\n * Email address is not supported by TikTok.\n *\n * :::\n *\n * :::tip\n *\n * AUTH_TIKTOK_ID will be the Client Key in the TikTok Application\n *\n * :::\n *\n * By default, Auth.js assumes that the TikTok provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The TikTok provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/tiktok.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * If You Need to Customize the TikTok Provider, You Can Use the Following Configuration as a custom provider\n *\n * ```ts\n * {\n *   async [customFetch](...args) {\n *     const url = new URL(args[0] instanceof Request ? args[0].url : args[0]);\n *     if (url.pathname.endsWith(\"/token/\")) {\n *       const [url, request] = args;\n *       const customHeaders = {\n *         ...request?.headers,\n *         \"content-type\": \"application/x-www-form-urlencoded\",\n *       };\n *\n *       const customBody = new URLSearchParams(request?.body as string);\n *       customBody.append(\"client_key\", process.env.AUTH_TIKTOK_ID!);\n *\n *       const response = await fetch(url, {\n *         ...request,\n *         headers: customHeaders,\n *         body: customBody.toString(),\n *       });\n *       const json = await response.json();\n *       return Response.json({ ...json });\n *     }\n *     return fetch(...args);\n *   },\n *\n *   id: \"tiktok\",\n *   name: \"TikTok\",\n *   type: \"oauth\",\n *   client: {\n *     token_endpoint_auth_method: \"client_secret_post\",\n *   },\n *\n *   authorization: {\n *     url: \"https://www.tiktok.com/v2/auth/authorize\",\n *     params: {\n *       client_key: options.clientId,\n *       scope: \"user.info.profile\", //Add scopes you need eg(user.info.profile,user.info.stats,video.list)\n *     },\n *   },\n *\n *   token: \"https://open.tiktokapis.com/v2/oauth/token/\",\n *\n *   userinfo: \"https://open.tiktokapis.com/v2/user/info/?fields=open_id,avatar_url,display_name,username\", //Add fields you need eg(open_id,avatar_url,display_name,username)\n *\n *   profile(profile) {\n *     return {\n *       id: profile.data.user.open_id,\n *       name: profile.data.user.display_name,\n *       image: profile.data.user.avatar_url,\n *       email: profile.data.user.email || profile.data.user.username || null,\n *     };\n *   },\n * }\n *\n * ```\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function TikTok(\n  options: OAuthUserConfig<TiktokProfile>\n): OAuthConfig<TiktokProfile> {\n  return {\n    async [customFetch](...args) {\n      const url = new URL(args[0] instanceof Request ? args[0].url : args[0])\n      if (url.pathname.endsWith(\"/token/\")) {\n        const [url, request] = args\n\n        const customHeaders = {\n          ...request?.headers,\n          \"content-type\": \"application/x-www-form-urlencoded\",\n        }\n\n        const customBody = new URLSearchParams(request?.body as string)\n        customBody.append(\"client_key\", options.clientId!)\n        const response = await fetch(url, {\n          ...request,\n          headers: customHeaders,\n          body: customBody.toString(),\n        })\n        const json = await response.json()\n        return Response.json({ ...json })\n      }\n      return fetch(...args)\n    },\n    id: \"tiktok\",\n    name: \"TikTok\",\n    type: \"oauth\",\n    client: {\n      token_endpoint_auth_method: \"client_secret_post\",\n    },\n    authorization: {\n      url: \"https://www.tiktok.com/v2/auth/authorize\",\n      params: {\n        client_key: options.clientId,\n        scope: \"user.info.basic\",\n      },\n    },\n\n    token: \"https://open.tiktokapis.com/v2/oauth/token/\",\n    userinfo:\n      \"https://open.tiktokapis.com/v2/user/info/?fields=open_id,avatar_url,display_name\",\n\n    profile(profile) {\n      return {\n        id: profile.data.user.open_id,\n        name: profile.data.user.display_name,\n        image: profile.data.user.avatar_url,\n        // Email address is not supported by TikTok.\n        email: null,\n      }\n    },\n    style: {\n      bg: \"#000\",\n      text: \"#fff\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/todoist.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Todoist</b> integration.</span>\n * <a href=\"https://www.todoist.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/todoist.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/todoist\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * @see https://developer.todoist.com/sync/v9/#user\n */\ninterface TodoistProfile extends Record<string, any> {\n  avatar_big: string\n  email: string\n  full_name: string\n  id: string\n}\n\n/**\n * Add Todoist login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/todoist\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Todoist from \"@auth/core/providers/todoist\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Todoist({\n *       clientId: TODOIST_CLIENT_ID,\n *       clientSecret: TODOIST_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Todoist OAuth documentation](https://developer.todoist.com/guides/#oauth)\n * - [Todoist configuration](https://developer.todoist.com/appconsole.html)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Todoist provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Todoist provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/todoist.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function TodoistProvider<P extends TodoistProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"todoist\",\n    name: \"Todoist\",\n    type: \"oauth\",\n    authorization: {\n      url: \"https://todoist.com/oauth/authorize\",\n      params: { scope: \"data:read\" },\n    },\n    token: \"https://todoist.com/oauth/access_token\",\n    client: {\n      token_endpoint_auth_method: \"client_secret_post\",\n    },\n    userinfo: {\n      async request({ tokens }) {\n        // To obtain Todoist user info, we need to call the Sync API\n        // See https://developer.todoist.com/sync/v9\n        const res = await fetch(\"https://api.todoist.com/sync/v9/sync\", {\n          method: \"POST\",\n          headers: {\n            Authorization: `Bearer ${tokens.access_token}`,\n            \"Content-Type\": \"application/json\",\n          },\n          body: JSON.stringify({\n            sync_token: \"*\",\n            resource_types: '[\"user\"]',\n          }),\n        })\n\n        const { user: profile } = await res.json()\n        return profile\n      },\n    },\n    profile(profile) {\n      return {\n        id: profile.id,\n        email: profile.email,\n        name: profile.full_name,\n        image: profile.avatar_big,\n      }\n    },\n    style: { text: \"#000\", bg: \"#E44332\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/trakt.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Trakt</b> integration.</span>\n * <a href=\"https://www.trakt.tv/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/trakt.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/trakt\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport interface TraktUser extends Record<string, any> {\n  username: string\n  private: boolean\n  name: string\n  vip: boolean\n  vip_ep: boolean\n  ids: { slug: string }\n  joined_at: string\n  location: string | null\n  about: string | null\n  gender: string | null\n  age: number | null\n  images: { avatar: { full: string } }\n}\n\n/**\n * Add Trakt login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/trakt\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Trakt from \"@auth/core/providers/trakt\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Trakt({ clientId: TRAKT_CLIENT_ID, clientSecret: TRAKT_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Trakt OAuth documentation](https://trakt.docs.apiary.io/#reference/authentication-oauth)\n *\n * If you're using the api in production by calling `api.trakt.tv`. Follow the example. If you wish to develop on Trakt's sandbox environment by calling `api-staging.trakt.tv`, change the URLs.\n *\n * Start by creating an OAuth app on Trakt for production or development. Then set the Client ID and Client Secret as TRAKT_ID and TRAKT_SECRET in .env.\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Trakt provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::danger\n *\n * - Trakt does not allow hotlinking images. Even the authenticated user's profile picture.\n * - Trakt does not supply the authenticated user's email.\n *\n * :::\n *\n * :::tip\n *\n * The Trakt provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/trakt.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Trakt<P extends TraktUser>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"trakt\",\n    name: \"Trakt\",\n    type: \"oauth\",\n    authorization: \"https://trakt.tv/oauth/authorize?scope=\",\n    token: \"https://api.trakt.tv/oauth/token\",\n    userinfo: {\n      url: \"https://api.trakt.tv/users/me?extended=full\",\n      async request({ tokens, provider }) {\n        return await fetch(provider.userinfo?.url as URL, {\n          headers: {\n            Authorization: `Bearer ${tokens.access_token}`,\n            \"trakt-api-version\": \"2\",\n            \"trakt-api-key\": provider.clientId,\n          },\n        }).then(async (res) => await res.json())\n      },\n    },\n    profile(profile) {\n      return {\n        id: profile.ids.slug,\n        name: profile.name,\n        email: null, // trakt does not provide user emails\n        image: profile.images.avatar.full, // trakt does not allow hotlinking\n      }\n    },\n    style: { bg: \"#ED2224\", text: \"#fff\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/twitch.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Twitch</b> integration.</span>\n * <a href=\"https://www.twitch.tv/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/twitch.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/twitch\n */\nimport type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\nexport interface TwitchProfile extends Record<string, any> {\n  sub: string\n  preferred_username: string\n  email: string\n  picture: string\n}\n\n/**\n * Add Twitch login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/twitch\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Twitch from \"@auth/core/providers/twitch\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Twitch({ clientId: TWITCH_CLIENT_ID, clientSecret: TWITCH_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Twitch app documentation](https://dev.twitch.tv/console/apps)\n *\n * Add the following redirect URL into the console `http://<your-next-app-url>/api/auth/callback/twitch`\n *\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Twitch provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::tip\n *\n * The Twitch provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/twitch.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Twitch(\n  config: OIDCUserConfig<TwitchProfile>\n): OIDCConfig<TwitchProfile> {\n  return {\n    issuer: \"https://id.twitch.tv/oauth2\",\n    id: \"twitch\",\n    name: \"Twitch\",\n    type: \"oidc\",\n    client: { token_endpoint_auth_method: \"client_secret_post\" },\n    authorization: {\n      params: {\n        scope: \"openid user:read:email\",\n        claims: {\n          id_token: { email: null, picture: null, preferred_username: null },\n        },\n      },\n    },\n    token: {\n      async conform(response) {\n        const body = await response.json()\n        if (response.ok) {\n          if (typeof body.scope === \"string\") {\n            console.warn(\n              \"'scope' is a string. Redundant workaround, please open an issue.\"\n            )\n          } else if (Array.isArray(body.scope)) {\n            body.scope = body.scope.join(\" \")\n            return new Response(JSON.stringify(body), response)\n          } else if (\"scope\" in body) {\n            delete body.scope\n            return new Response(JSON.stringify(body), response)\n          }\n        } else {\n          const { message: error_description, error } = body\n          if (typeof error !== \"string\") {\n            return new Response(\n              JSON.stringify({ error: \"invalid_request\", error_description }),\n              response\n            )\n          }\n          console.warn(\n            \"Response has 'error'. Redundant workaround, please open an issue.\"\n          )\n        }\n      },\n    },\n    style: { bg: \"#65459B\", text: \"#fff\" },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/twitter.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Twitter</b> integration.</span>\n * <a href=\"https://www.x.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/twitter.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/twitter\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * [Users lookup](https://developer.x.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me)\n */\nexport interface TwitterProfile {\n  data: {\n    /**\n     * Unique identifier of this user. This is returned as a string in order to avoid complications with languages and tools\n     * that cannot handle large integers.\n     */\n    id: string\n    /** The friendly name of this user, as shown on their profile. */\n    name: string\n    /** @note Email is currently unsupported by Twitter.  */\n    email?: string\n    /** The Twitter handle (screen name) of this user. */\n    username: string\n    /**\n     * The location specified in the user's profile, if the user provided one.\n     * As this is a freeform value, it may not indicate a valid location, but it may be fuzzily evaluated when performing searches with location queries.\n     *\n     * To return this field, add `user.fields=location` in the authorization request's query parameter.\n     */\n    location?: string\n    /**\n     * This object and its children fields contain details about text that has a special meaning in the user's description.\n     *\n     *To return this field, add `user.fields=entities` in the authorization request's query parameter.\n     */\n    entities?: {\n      /** Contains details about the user's profile website. */\n      url: {\n        /** Contains details about the user's profile website. */\n        urls: Array<{\n          /** The start position (zero-based) of the recognized user's profile website. All start indices are inclusive. */\n          start: number\n          /** The end position (zero-based) of the recognized user's profile website. This end index is exclusive. */\n          end: number\n          /** The URL in the format entered by the user. */\n          url: string\n          /** The fully resolved URL. */\n          expanded_url: string\n          /** The URL as displayed in the user's profile. */\n          display_url: string\n        }>\n      }\n      /** Contains details about URLs, Hashtags, Cashtags, or mentions located within a user's description. */\n      description: {\n        hashtags: Array<{\n          start: number\n          end: number\n          tag: string\n        }>\n      }\n    }\n    /**\n     * Indicate if this user is a verified Twitter user.\n     *\n     * To return this field, add `user.fields=verified` in the authorization request's query parameter.\n     */\n    verified?: boolean\n    /**\n     * The text of this user's profile description (also known as bio), if the user provided one.\n     *\n     * To return this field, add `user.fields=description` in the authorization request's query parameter.\n     */\n    description?: string\n    /**\n     * The URL specified in the user's profile, if present.\n     *\n     * To return this field, add `user.fields=url` in the authorization request's query parameter.\n     */\n    url?: string\n    /** The URL to the profile image for this user, as shown on the user's profile. */\n    profile_image_url?: string\n    protected?: boolean\n    /**\n     * Unique identifier of this user's pinned Tweet.\n     *\n     *  You can obtain the expanded object in `includes.tweets` by adding `expansions=pinned_tweet_id` in the authorization request's query parameter.\n     */\n    pinned_tweet_id?: string\n    created_at?: string\n  }\n  includes?: {\n    tweets?: Array<{\n      id: string\n      text: string\n    }>\n  }\n  [claims: string]: unknown\n}\n\n/**\n * Add Twitter login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/twitter\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Twitter from \"@auth/core/providers/twitter\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Twitter({\n *       clientId: TWITTER_CLIENT_ID,\n *       clientSecret: TWITTER_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Twitter App documentation](https://developer.x.com/en/apps)\n *\n * ## OAuth 2\n * Twitter supports OAuth 2, which is currently opt-in. To enable it, simply add version: \"2.0\" to your Provider configuration:\n * ```ts\n * Twitter({\n *   clientId: process.env.TWITTER_ID,\n *   clientSecret: process.env.TWITTER_SECRET,\n *   version: \"2.0\", // opt-in to Twitter OAuth 2.0\n * })\n * ```\n * Keep in mind that although this change is easy, it changes how and with which of Twitter APIs you can interact with. Read the official Twitter OAuth 2 documentation for more details.\n *\n *\n * :::note\n *\n * Email is currently not supported by Twitter OAuth 2.0.\n *\n * :::\n *\n * ### Notes\n *\n * Twitter is currently the only built-in provider using the OAuth 1.0 spec.\n * This means that you won't receive an `access_token` or `refresh_token`, but an `oauth_token` and `oauth_token_secret` respectively. Remember to add these to your database schema, in case if you are using an [Adapter](https://authjs.dev/reference/core/adapters).\n *\n * :::tip\n *\n * You must enable the \"Request email address from users\" option in your app permissions if you want to obtain the users email address.\n *\n * :::\n *\n * By default, Auth.js assumes that the Twitter provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Twitter provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/twitter.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Twitter(\n  config: OAuthUserConfig<TwitterProfile>\n): OAuthConfig<TwitterProfile> {\n  return {\n    id: \"twitter\",\n    name: \"Twitter\",\n    type: \"oauth\",\n    checks: [\"pkce\", \"state\"],\n    authorization:\n      \"https://x.com/i/oauth2/authorize?scope=users.read tweet.read offline.access\",\n    token: \"https://api.x.com/2/oauth2/token\",\n    userinfo: \"https://api.x.com/2/users/me?user.fields=profile_image_url\",\n    profile({ data }) {\n      return {\n        id: data.id,\n        name: data.name,\n        email: data.email ?? null,\n        image: data.profile_image_url,\n      }\n    },\n    style: { bg: \"#1da1f2\", text: \"#fff\" },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/united-effects.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>United Effects</b> integration.</span>\n * <a href=\"https://www.unitedeffects.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/united-effects.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/united-effects\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\nexport interface UnitedEffectsProfile extends Record<string, any> {\n  sub: string\n  email: string\n}\n/**\n * Add United Effects login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/united-effects\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import UnitedEffects from \"@auth/core/providers/united-effects\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     UnitedEffects({ clientId: UE_CLIENT_ID, clientSecret: UE_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [UnitedEffects Auth.js documentation](https://docs.unitedeffects.com/integrations/nextauthjs)\",\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the UnitedEffects provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * :::note\n *\n * `issuer` should be the fully qualified URL including your Auth Group ID – e.g. `https://auth.unitedeffects.com/YQpbQV5dbW-224dCovz-3`\n *\n * :::\n *\n * :::danger\n *\n * The United Effects API does not return the user name or image by design, so this provider will return null for both. United Effects prioritizes user personal information security above all and has built a secured profile access request system separate from the provider API.\n *\n * :::\n *\n * :::tip\n *\n * The UnitedEffects provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/united-effects.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function UnitedEffects<P extends UnitedEffectsProfile>(\n  options: OAuthUserConfig<P> & { issuer: string }\n): OAuthConfig<P> {\n  return {\n    id: \"united-effects\",\n    name: \"United Effects\",\n    type: \"oidc\",\n    authorization: {\n      params: { scope: \"openid email profile\", resource: options.issuer },\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/vipps.ts",
    "content": "import type { OIDCConfig, OIDCUserConfig } from \"./index.js\"\n\ninterface Address {\n  address_type: string\n  country: string\n  formatted: string\n  postal_code: string\n  region: string\n  street_address: string\n}\n\n/** @see [User Profile Structure](https://developer.vippsmobilepay.com/api/userinfo/#operation/userinfoAuthorizationCode) */\nexport interface VippsProfile extends Record<string, any> {\n  accounts: {\n    account_name: string\n    account_number: number\n    bank_name: string\n  }[]\n  address: Address\n  other_addresses: Address[]\n  birthdate: string\n  email: string\n  email_verified: boolean\n  family_name: string\n  given_name: string\n  name: string\n  nin: string\n  phone_number: string\n  sid: string\n  sub: string\n  delegatedConsents: {\n    language: string\n    heading: string\n    termsDescription: string\n    confirmConsentButtonText: string\n    links: {\n      termsLinkText: string\n      termsLinkUrl: string\n      privacyStatementLinkText: string\n      privacyStatementLinkUrl: string\n    }\n    timeOfConsent: string\n    consents: {\n      id: string\n      accepted: boolean\n      required: boolean\n      textDisplayedToUser: string\n    }[]\n  }\n}\n\n/**\n * @see [Vipps Login API](https://developer.vippsmobilepay.com/docs/APIs/login-api/api-guide)\n *\n * ## Example\n *\n * ```ts\n * import Vipps from \"@auth/core/providers/vipps\"\n * ...\n * providers: [\n *  Vipps({\n *    clientId: process.env.AUTH_VIPPS_ID,\n *    clientSecret: process.env.AUTH_VIPPS_SECRET,\n *  })\n * ]\n * ...\n * ```\n * ::: note\n * If you're testing, make sure to override the issuer option with apitest.vipps.no\n * :::\n */\nexport default function Vipps(\n  options: OIDCUserConfig<VippsProfile>\n): OIDCConfig<VippsProfile> {\n  return {\n    id: \"vipps\",\n    name: \"Vipps\",\n    type: \"oidc\",\n    issuer: \"https://api.vipps.no/access-management-1.0/access/\",\n    authorization: { params: { scope: \"openid name email\" } },\n    idToken: false,\n    style: { brandColor: \"#f05c18\" },\n    checks: [\"pkce\", \"state\", \"nonce\"],\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/vk.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>VK</b> integration.</span>\n * <a href=\"https://vk.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/vk.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/vk\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/** https://dev.vk.com/reference/objects/user */\nexport interface VkProfile {\n  id: number\n  first_name: string\n  last_name: string\n  photo_100: string\n  can_access_closed: boolean\n  is_closed: boolean\n  deactivated?: string\n  sex?: 0 | 1 | 2\n  screen_name?: string\n  photo_50?: string\n  online?: 0 | 1\n  online_mobile?: 0 | 1\n  online_app?: number\n  verified?: 0 | 1\n  trending?: 0 | 1\n  friend_status?: 0 | 1 | 2 | 3\n  first_name_nom?: string\n  first_name_gen?: string\n  first_name_dat?: string\n  first_name_acc?: string\n  first_name_ins?: string\n  first_name_abl?: string\n  last_name_nom?: string\n  last_name_gen?: string\n  last_name_dat?: string\n  last_name_acc?: string\n  last_name_ins?: string\n  last_name_abl?: string\n  nickname?: string\n  maiden_name?: string\n  domain?: string\n  bdate?: string\n  city?: {\n    id: number\n    title: string\n  }\n  country?: {\n    id: number\n    title: string\n  }\n  timezone?: number\n  photo_200?: string\n  photo_max?: string\n  photo_200_orig?: string\n  photo_400_orig?: string\n  photo_max_orig?: string\n  photo_id?: string\n  has_photo?: 0 | 1\n  has_mobile?: 0 | 1\n  is_friend?: 0 | 1\n  can_post?: 0 | 1\n  can_see_all_posts?: 0 | 1\n  can_see_audio?: 0 | 1\n  connections?: {\n    facebook?: string\n    skype?: string\n    twitter?: string\n    livejournal?: string\n    instagram?: string\n  }\n  photo_400?: string\n  wall_default?: \"owner\" | \"all\"\n  interests?: string\n  books?: string\n  tv?: string\n  quotes?: string\n  about?: string\n  games?: string\n  movies?: string\n  activities?: string\n  music?: string\n  can_write_private_message?: 0 | 1\n  can_send_friend_request?: 0 | 1\n  contacts?: {\n    mobile_phone?: string\n    home_phone?: string\n  }\n  site?: string\n  status_audio?: {\n    access_key?: string\n    artist: string\n    id: number\n    owner_id: number\n    title: string\n    url?: string\n    duration: number\n    date?: number\n    album_id?: number\n    genre_id?: number\n    performer?: string\n  }\n  status?: string\n  last_seen?: {\n    platform?: 1 | 2 | 3 | 4 | 5 | 6 | 7\n    time?: number\n  }\n  exports?: {\n    facebook?: number\n    livejournal?: number\n    twitter?: number\n    instagram?: number\n  }\n  crop_photo?: {\n    photo: {\n      access_key?: string\n      album_id: number\n      date: number\n      height?: number\n      id: number\n      images?: Array<{\n        height?: number\n        type?: \"s\" | \"m\" | \"x\" | \"l\" | \"o\" | \"p\" | \"q\" | \"r\" | \"y\" | \"z\" | \"w\"\n        url?: string\n        width?: number\n      }>\n      lat?: number\n      long?: number\n      owner_id: number\n      photo_256?: string\n      can_comment?: 0 | 1\n      place?: string\n      post_id?: number\n      sizes?: Array<{\n        height: number\n        url: string\n        src?: string\n        type:\n          | \"s\"\n          | \"m\"\n          | \"x\"\n          | \"o\"\n          | \"p\"\n          | \"q\"\n          | \"r\"\n          | \"k\"\n          | \"l\"\n          | \"y\"\n          | \"z\"\n          | \"c\"\n          | \"w\"\n          | \"a\"\n          | \"b\"\n          | \"e\"\n          | \"i\"\n          | \"d\"\n          | \"j\"\n          | \"temp\"\n          | \"h\"\n          | \"g\"\n          | \"n\"\n          | \"f\"\n          | \"max\"\n        width: number\n      }>\n      text?: string\n      user_id?: number\n      width?: number\n      has_tags: boolean\n    }\n    crop: {\n      x: number\n      y: number\n      x2: number\n      y2: number\n    }\n    rect: {\n      x: number\n      y: number\n      x2: number\n      y2: number\n    }\n  }\n  followers_count?: number\n  blacklisted?: 0 | 1\n  blacklisted_by_me?: 0 | 1\n  is_favorite?: 0 | 1\n  is_hidden_from_feed?: 0 | 1\n  common_count?: number\n  occupation?: {\n    id?: number\n    name?: string\n    type?: \"work\" | \"school\" | \"university\"\n  }\n  career?: {\n    group_id?: number\n    company?: string\n    country_id?: number\n    city_id?: number\n    city_name?: string\n    from?: number\n    until?: number\n    position?: string\n  }\n  military?: {\n    country_id: number\n    from?: number\n    unit: string\n    unit_id: number\n    until?: number\n  }\n  education?: {\n    university?: number\n    university_name?: string\n    faculty?: number\n    faculty_name?: string\n    graduation?: number\n  }\n  home_town?: string\n  relation?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8\n  relation_partner?: {\n    deactivated?: string\n    first_name: string\n    hidden?: number\n    id: number\n    last_name: string\n    can_access_closed?: boolean\n    is_closed?: boolean\n  }\n  personal?: {\n    alcohol?: 1 | 2 | 3 | 4 | 5\n    inspired_by?: string\n    langs?: string[]\n    life_main?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8\n    people_main?: 1 | 2 | 3 | 4 | 5 | 6\n    political?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9\n    religion?: string\n    smoking?: 1 | 2 | 3 | 4 | 5\n  }\n  universities?: Array<{\n    chair?: number\n    chair_name?: string\n    city?: number\n    country?: number\n    education_form?: string\n    education_status?: string\n    faculty?: number\n    faculty_name?: string\n    graduation?: number\n    id?: number\n    name?: string\n    university_group_id?: number\n  }>\n  schools?: Array<{\n    city?: number\n    class?: string\n    country?: number\n    id?: string\n    name?: string\n    type?: number\n    type_str?: string\n    year_from?: number\n    year_graduated?: number\n    year_to?: number\n    speciality?: string\n  }>\n  relatives?: Array<{\n    id?: number\n    name?: string\n    type: \"parent\" | \"child\" | \"grandparent\" | \"grandchild\" | \"sibling\"\n  }>\n  counters?: {\n    albums?: number\n    videos?: number\n    audios?: number\n    photos?: number\n    notes?: number\n    friends?: number\n    groups?: number\n    online_friends?: number\n    mutual_friends?: number\n    user_videos?: number\n    followers?: number\n    pages?: number\n  }\n  is_no_index?: 0 | 1\n  // Expand from token https://dev.vk.com/en/reference/access-rights?ref=old_portal\n  email?: string\n}\n\n/**\n * Add VK login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/vk\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import VK from \"@auth/core/providers/vk\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [VK({ clientId: VK_CLIENT_ID, clientSecret: VK_CLIENT_SECRET })],\n * })\n * ```\n *\n * ### Resources\n *\n * - [VK API documentation](https://vk.com/dev/first_guide)\n * - [VK App configuration](https://vk.com/apps?act=manage)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the VK provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The VK provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/vk.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::note\n *\n * By default the provider uses 5.126 version of the API. See https://vk.com/dev/versions for more info.\n * If you want to use a different version, you can pass it to provider's options object:\n * ```ts\n * const apiVersion = \"5.126\"\n * providers: [\n *   Vk({\n *     accessTokenUrl: `https://oauth.vk.com/access_token?v=${apiVersion}`,\n *     requestTokenUrl: `https://oauth.vk.com/access_token?v=${apiVersion}`,\n *     authorizationUrl:\n *       `https://oauth.vk.com/authorize?response_type=code&v=${apiVersion}`,\n *     profileUrl: `https://api.vk.com/method/users.get?fields=photo_100&v=${apiVersion}`,\n *   })\n * ]\n * ```\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function VK<P extends Record<string, any> = VkProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  const apiVersion = \"5.131\" // https://vk.com/dev/versions\n\n  return {\n    id: \"vk\",\n    name: \"VK\",\n    type: \"oauth\",\n    authorization: `https://oauth.vk.com/authorize?scope=email&v=${apiVersion}`,\n    client: {\n      token_endpoint_auth_method: \"client_secret_post\",\n    },\n    token: `https://oauth.vk.com/access_token?v=${apiVersion}`,\n    userinfo: {\n      url: `https://api.vk.com/method/users.get?fields=photo_100&v=${apiVersion}`,\n      async request({ tokens, provider }) {\n        const profile = await fetch(provider.userinfo?.url as URL, {\n          headers: {\n            Authorization: `Bearer ${tokens.access_token}`,\n            \"User-Agent\": \"authjs\",\n          },\n        }).then(async (res) => await res.json())\n\n        profile.response[0].email = tokens.email ? tokens.email : null\n\n        return profile.response[0]\n      },\n    },\n    profile(profile: P) {\n      return {\n        id: profile.id,\n        name: [profile.first_name, profile.last_name].filter(Boolean).join(\" \"),\n        email: profile.email ?? null,\n        image: profile.photo_100,\n      }\n    },\n    style: { bg: \"#07F\", text: \"#fff\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/webauthn.ts",
    "content": "import {\n  generateAuthenticationOptions,\n  generateRegistrationOptions,\n  verifyAuthenticationResponse,\n  verifyRegistrationResponse,\n} from \"@simplewebauthn/server\"\nimport { MissingAdapter } from \"../errors.js\"\n\nimport type { CommonProviderOptions, CredentialInput } from \"./index.js\"\nimport type {\n  GenerateRegistrationOptionsOpts,\n  GenerateAuthenticationOptionsOpts,\n  VerifyAuthenticationResponseOpts,\n  VerifyRegistrationResponseOpts,\n} from \"@simplewebauthn/server\"\n\nimport type {\n  InternalOptions,\n  RequestInternal,\n  SemverString,\n  User,\n} from \"../types.js\"\n\nexport type WebAuthnProviderType = \"webauthn\"\n\nexport const DEFAULT_WEBAUTHN_TIMEOUT = 5 * 60 * 1000 // 5 minutes\nexport const DEFAULT_SIMPLEWEBAUTHN_BROWSER_VERSION: SemverString = \"v9.0.1\"\n\nexport type RelayingParty = {\n  /** Relaying Party ID. Use the website's domain name. */\n  id: string\n  /** Relaying Party name. Use the website's name. */\n  name: string\n  /** Relaying Party origin. Use the website's origin. */\n  origin: string\n}\n\ntype RelayingPartyArray = {\n  /** Relaying Party ID. Use the website's domain name. */\n  id: string | string[]\n  /** Relaying Party name. Use the website's name. */\n  name: string | string[]\n  /** Relaying Party origin. Use the website's origin. */\n  origin: string | string[]\n}\n\nexport type GetUserInfo = (\n  options: InternalOptions<WebAuthnProviderType>,\n  request: RequestInternal\n) => Promise<\n  | { user: User; exists: true }\n  | { user: Omit<User, \"id\">; exists: false }\n  | null\n>\n\ntype ConfigurableAuthenticationOptions = Omit<\n  GenerateAuthenticationOptionsOpts,\n  \"rpID\" | \"allowCredentials\" | \"challenge\"\n>\ntype ConfigurableRegistrationOptions = Omit<\n  GenerateRegistrationOptionsOpts,\n  | \"rpName\"\n  | \"rpID\"\n  | \"userID\"\n  | \"userName\"\n  | \"challenge\"\n  | \"userDisplayName\"\n  | \"excludeCredentials\"\n>\ntype ConfigurableVerifyAuthenticationOptions = Omit<\n  VerifyAuthenticationResponseOpts,\n  | \"expectedChallenge\"\n  | \"expectedOrigin\"\n  | \"expectedRPID\"\n  | \"authenticator\"\n  | \"response\"\n>\ntype ConfigurableVerifyRegistrationOptions = Omit<\n  VerifyRegistrationResponseOpts,\n  \"expectedChallenge\" | \"expectedOrigin\" | \"expectedRPID\" | \"response\"\n>\n\nexport interface WebAuthnConfig extends CommonProviderOptions {\n  type: WebAuthnProviderType\n  /**\n   * Relaying party (RP) configuration\n   *\n   * If not provided, the request URL will be used.\n   **/\n  relayingParty?: Partial<RelayingPartyArray>\n  /**\n   * Function that returns the relaying party for the current request.\n   */\n  getRelayingParty: (\n    options: InternalOptions<WebAuthnProviderType>,\n    request: RequestInternal\n  ) => RelayingParty\n  /**\n   * Enable conditional UI.\n   *\n   * NOTE: Only one provider can have this option enabled at a time. Defaults to `true`.\n   */\n  enableConditionalUI: boolean\n  /**\n   * Version of SimpleWebAuthn browser script to load in the sign in page.\n   *\n   * This is only loaded if the provider has conditional UI enabled. If set to false, it won't load any script.\n   * Defaults to `v9.0.0`.\n   */\n  simpleWebAuthnBrowserVersion: SemverString | false\n  /** Form fields displayed in the default Passkey sign in/up form.\n   * These are not validated or enforced beyond the default Auth.js authentication page.\n   *\n   * By default it displays an email field.\n   */\n  formFields: Record<string, CredentialInput>\n  /**\n   * Authentication options that are passed to @simplewebauthn during authentication.\n   */\n  authenticationOptions?: Partial<ConfigurableAuthenticationOptions>\n  /**\n   * Registration options that are passed to @simplewebauthn during registration.\n   */\n  registrationOptions: Partial<ConfigurableRegistrationOptions>\n  /**\n   * Verify Authentication options that are passed to @simplewebauthn during authentication.\n   */\n  verifyAuthenticationOptions?: Partial<ConfigurableVerifyAuthenticationOptions>\n  /**\n   * Verify Registration options that are passed to @simplewebauthn during registration.\n   */\n  verifyRegistrationOptions?: Partial<ConfigurableVerifyRegistrationOptions>\n  /**\n   * Function that returns the user info that the authenticator will use during registration and authentication.\n   *\n   * - It accepts the provider options, the request object, and returns the user info.\n   * - If the request contains an existing user's data (e.g. email address), the function must return the existing user and `exists` must be `true`.\n   * - If the request contains enough information to create a new user, the function must return a new user info and `exists` must be `false`.\n   * - If the request does not contain enough information to create a new user, the function must return `null`.\n   *\n   * It should not have any side effects (i.e. it shall not modify the database).\n   *\n   * During passkey creation:\n   *  - The passkey's user ID will be a random string.\n   *  - The passkey's user name will be user.email\n   *  - The passkey's user display name will be user.name, if present, or user.email\n   *\n   * By default, it looks for and uses the \"email\" request parameter to look up the user in the database.\n   */\n  getUserInfo: GetUserInfo\n  /** SimpleWebAuthn instance to use for registration and authentication. */\n  simpleWebAuthn: {\n    verifyAuthenticationResponse: typeof verifyAuthenticationResponse\n    verifyRegistrationResponse: typeof verifyRegistrationResponse\n    generateAuthenticationOptions: typeof generateAuthenticationOptions\n    generateRegistrationOptions: typeof generateRegistrationOptions\n  }\n}\n\n/**\n * Add WebAuthn login to your page.\n *\n * ### Setup\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import WebAuthn from \"@auth/core/providers/webauthn\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [WebAuthn],\n * })\n * ```\n * ### Resources\n *\n * - [SimpleWebAuthn - Server side](https://simplewebauthn.dev/docs/packages/server)\n * - [SimpleWebAuthn - Client side](https://simplewebauthn.dev/docs/packages/client)\n * - [Source code](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/webauthn.ts)\n *\n * :::tip\n *\n * The WebAuthn provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/webauthn.ts).\n * To override the defaults for your use case, check out [customizing the built-in WebAuthn provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function WebAuthn(\n  config: Partial<WebAuthnConfig>\n): WebAuthnConfig {\n  return {\n    id: \"webauthn\",\n    name: \"WebAuthn\",\n    enableConditionalUI: true,\n    simpleWebAuthn: {\n      generateAuthenticationOptions,\n      generateRegistrationOptions,\n      verifyAuthenticationResponse,\n      verifyRegistrationResponse,\n    },\n    authenticationOptions: { timeout: DEFAULT_WEBAUTHN_TIMEOUT },\n    registrationOptions: { timeout: DEFAULT_WEBAUTHN_TIMEOUT },\n    formFields: {\n      email: {\n        label: \"Email\",\n        required: true,\n        autocomplete: \"username webauthn\",\n      },\n    },\n    simpleWebAuthnBrowserVersion: DEFAULT_SIMPLEWEBAUTHN_BROWSER_VERSION,\n    getUserInfo,\n    getRelayingParty,\n    ...config,\n    type: \"webauthn\",\n  }\n}\n\n/**\n * Retrieves user information for the WebAuthn provider.\n *\n * It looks for the \"email\" query parameter and uses it to look up the user in the database.\n * It also accepts a \"name\" query parameter to set the user's display name.\n *\n * @param options - The internaloptions object.\n * @param request - The request object containing the query parameters.\n * @returns The existing or new user info.\n * @throws {MissingAdapter} If the adapter is missing.\n * @throws {EmailSignInError} If the email address is not provided.\n */\nconst getUserInfo: GetUserInfo = async (options, request) => {\n  const { adapter } = options\n  if (!adapter)\n    throw new MissingAdapter(\n      \"WebAuthn provider requires a database adapter to be configured\"\n    )\n\n  // Get email address from the query.\n  const { query, body, method } = request\n  const email = (method === \"POST\" ? body?.email : query?.email) as unknown\n\n  // If email is not provided, return null\n  if (!email || typeof email !== \"string\") return null\n\n  const existingUser = await adapter.getUserByEmail(email)\n  if (existingUser) {\n    return { user: existingUser, exists: true }\n  }\n\n  // If the user does not exist, return a new user info.\n  return { user: { email }, exists: false }\n}\n\n/**\n * Retrieves the relaying party information based on the provided options.\n * If the relaying party information is not provided, it falls back to using the URL information.\n */\nfunction getRelayingParty(\n  /** The options object containing the provider and URL information. */\n  options: InternalOptions<WebAuthnProviderType>\n): RelayingParty {\n  const { provider, url } = options\n  const { relayingParty } = provider\n\n  const id = Array.isArray(relayingParty?.id)\n    ? relayingParty.id[0]\n    : relayingParty?.id\n\n  const name = Array.isArray(relayingParty?.name)\n    ? relayingParty.name[0]\n    : relayingParty?.name\n  const origin = Array.isArray(relayingParty?.origin)\n    ? relayingParty.origin[0]\n    : relayingParty?.origin\n\n  return {\n    id: id ?? url.hostname,\n    name: name ?? url.host,\n    origin: origin ?? url.origin,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/webex.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Webex</b> integration.</span>\n * <a href=\"https://webex.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/webex.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/webex\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * The returned user profile from Webex when using the profile callback.\n *\n * Please refer to {@link https://developer.webex.com/docs/api/v1/people/get-my-own-details People - Get My Own Details}\n * on Webex Developer portal for additional fields. Returned fields may vary depending on the user's role, the OAuth\n * integration's scope, and the organization the OAuth integration belongs to.\n */\nexport interface WebexProfile extends Record<string, any> {\n  id: string\n  emails: string[]\n  displayName?: string\n  avatar?: string\n}\n\n/**\n * Add Webex login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/webex\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Webex from \"@auth/core/providers/webex\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Webex({ clientId: WEBEX_CLIENT_ID, clientSecret: WEBEX_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Webex OAuth 2.0 Integration Guide](https://developer.webex.com/docs/integrations)\n * - [Login with Webex](https://developer.webex.com/docs/login-with-webex)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Webex provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Webex provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/webex.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Webex<P extends WebexProfile>(\n  config: OAuthUserConfig<P> & { apiBaseUrl?: string }\n): OAuthConfig<P> {\n  const apiBaseUrl = config?.apiBaseUrl ?? \"https://webexapis.com/v1\"\n\n  return {\n    id: \"webex\",\n    name: \"Webex\",\n    type: \"oauth\",\n    authorization: {\n      url: `${apiBaseUrl}/authorize`,\n      params: { scope: \"spark:kms spark:people_read\" },\n    },\n    token: `${apiBaseUrl}/access_token`,\n    userinfo: `${apiBaseUrl}/people/me`,\n    profile(profile) {\n      return {\n        id: profile.id,\n        email: profile.emails[0],\n        name: profile.displayName,\n        image: profile.avatar,\n      }\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/wechat.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#24292f\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>WeChat</b> integration.</span>\n * <a href=\"https://www.wechat.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/wechat.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/wechat\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/** @see [Get the authenticated user](https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Authorized_Interface_Calling_UnionID.html) */\nexport interface WeChatProfile {\n  openid: string\n  nickname: string\n  sex: number\n  province: string\n  city: string\n  country: string\n  headimgurl: string\n  privilege: string[]\n  unionid: string\n  [claim: string]: unknown\n}\n\n/**\n * Add WeChat login to your page and make requests to [WeChat APIs](https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Authorized_Interface_Calling_UnionID.html).\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/wechat\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import WeChat from \"@auth/core/providers/wechat\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [WeChat({\n *     clientId: AUTH_WECHAT_APP_ID,\n *     clientSecret: AUTH_WECHAT_APP_SECRET,\n *     platformType: \"OfficialAccount\",\n *   })],\n * })\n * ```\n *\n * ### Resources\n *\n * - [WeChat Official Account](https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html)\n * - [WeChat Official Account - Webpage Authorization](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html)\n * - [WeChat Official Account Test Account](https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login)\n * - [WeChat WebsiteApp Login](https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html)\n * - [使用微信测试账号对网页进行授权](https://cloud.tencent.com/developer/article/1703167)\n *\n * :::tip\n *\n * The WeChat provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/wechat.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\n\nexport default function WeChat(\n  options: OAuthUserConfig<WeChatProfile> & {\n    platformType?: \"OfficialAccount\" | \"WebsiteApp\"\n  }\n): OAuthConfig<WeChatProfile> {\n  const { clientId, clientSecret, platformType = \"OfficialAccount\" } = options\n\n  return {\n    id: \"wechat\",\n    name: \"WeChat\",\n    type: \"oauth\",\n    style: { logo: \"/wechat.svg\", bg: \"#fff\", text: \"#000\" },\n    checks: [\"state\"],\n    authorization: {\n      url:\n        platformType === \"OfficialAccount\"\n          ? \"https://open.weixin.qq.com/connect/oauth2/authorize\"\n          : \"https://open.weixin.qq.com/connect/qrconnect\",\n      params: {\n        appid: clientId,\n        scope:\n          platformType === \"OfficialAccount\"\n            ? \"snsapi_userinfo\"\n            : \"snsapi_login\",\n      },\n    },\n    token: {\n      url: \"https://api.weixin.qq.com/sns/oauth2/access_token\",\n      params: { appid: clientId, secret: clientSecret },\n      async conform(response) {\n        const data = await response.json()\n        if (data.token_type === \"bearer\") {\n          console.warn(\n            \"token_type is 'bearer'. Redundant workaround, please open an issue.\"\n          )\n          return response\n        }\n        return Response.json({ ...data, token_type: \"bearer\" }, response)\n      },\n    },\n    userinfo: {\n      url: \"https://api.weixin.qq.com/sns/userinfo\",\n      async request({ tokens, provider }) {\n        if (!provider.userinfo) return\n\n        const url = new URL(provider.userinfo.url)\n        url.searchParams.set(\"access_token\", tokens.access_token!)\n        url.searchParams.set(\"openid\", String(tokens.openid))\n        url.searchParams.set(\"lang\", \"zh_CN\")\n        const response = await fetch(url)\n        return response.json()\n      },\n    },\n    profile(profile) {\n      return {\n        id: profile.unionid,\n        name: profile.nickname,\n        email: null,\n        image: profile.headimgurl,\n      }\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/wikimedia.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b> Wikimedia</b> integration.</span>\n * <a href=\"https://mediawiki.org/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/wikimedia.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/wikimedia\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\nexport type WikimediaGroup =\n  | \"*\"\n  | \"user\"\n  | \"autoconfirmed\"\n  | \"extendedconfirmed\"\n  | \"bot\"\n  | \"sysop\"\n  | \"bureaucrat\"\n  | \"steward\"\n  | \"accountcreator\"\n  | \"import\"\n  | \"transwiki\"\n  | \"ipblock-exempt\"\n  | \"oversight\"\n  | \"rollbacker\"\n  | \"propertycreator\"\n  | \"wikidata-staff\"\n  | \"flood\"\n  | \"translationadmin\"\n  | \"confirmed\"\n  | \"flow-bot\"\n  | \"checkuser\"\n\nexport type WikimediaGrant =\n  | \"basic\"\n  | \"blockusers\"\n  | \"checkuser\"\n  | \"createaccount\"\n  | \"delete\"\n  | \"editinterface\"\n  | \"editmycssjs\"\n  | \"editmyoptions\"\n  | \"editmywatchlist\"\n  | \"editpage\"\n  | \"editprotected\"\n  | \"editsiteconfig\"\n  | \"globalblock\"\n  | \"highvolume\"\n  | \"import\"\n  | \"mergehistory\"\n  | \"oath\"\n  | \"oversight\"\n  | \"patrol\"\n  | \"privateinfo\"\n  | \"protect\"\n  | \"rollback\"\n  | \"sendemail\"\n  | \"shortenurls\"\n  | \"uploadfile\"\n  | \"viewdeleted\"\n  | \"viewmywatchlist\"\n\nexport type WikimediaRight =\n  | \"abusefilter-log\"\n  | \"apihighlimits\"\n  | \"applychangetags\"\n  | \"autoconfirmed\"\n  | \"autopatrol\"\n  | \"autoreview\"\n  | \"bigdelete\"\n  | \"block\"\n  | \"blockemail\"\n  | \"bot\"\n  | \"browsearchive\"\n  | \"changetags\"\n  | \"checkuser\"\n  | \"checkuser-log\"\n  | \"createaccount\"\n  | \"createpage\"\n  | \"createpagemainns\"\n  | \"createtalk\"\n  | \"delete\"\n  | \"delete-redirect\"\n  | \"deletedhistory\"\n  | \"deletedtext\"\n  | \"deletelogentry\"\n  | \"deleterevision\"\n  | \"edit\"\n  | \"edit-legal\"\n  | \"editinterface\"\n  | \"editmyoptions\"\n  | \"editmyusercss\"\n  | \"editmyuserjs\"\n  | \"editmyuserjson\"\n  | \"editmywatchlist\"\n  | \"editprotected\"\n  | \"editsemiprotected\"\n  | \"editsitecss\"\n  | \"editsitejs\"\n  | \"editsitejson\"\n  | \"editusercss\"\n  | \"edituserjs\"\n  | \"edituserjson\"\n  | \"globalblock\"\n  | \"import\"\n  | \"importupload\"\n  | \"ipblock-exempt\"\n  | \"item-merge\"\n  | \"item-redirect\"\n  | \"item-term\"\n  | \"markbotedits\"\n  | \"massmessage\"\n  | \"mergehistory\"\n  | \"minoredit\"\n  | \"move\"\n  | \"move-subpages\"\n  | \"movefile\"\n  | \"movestable\"\n  | \"mwoauth-authonlyprivate\"\n  | \"nominornewtalk\"\n  | \"noratelimit\"\n  | \"nuke\"\n  | \"patrol\"\n  | \"patrolmarks\"\n  | \"property-create\"\n  | \"property-term\"\n  | \"protect\"\n  | \"purge\"\n  | \"read\"\n  | \"reupload\"\n  | \"reupload-own\"\n  | \"reupload-shared\"\n  | \"rollback\"\n  | \"sendemail\"\n  | \"skipcaptcha\"\n  | \"suppressionlog\"\n  | \"tboverride\"\n  | \"templateeditor\"\n  | \"torunblocked\"\n  | \"transcode-reset\"\n  | \"translate\"\n  | \"undelete\"\n  | \"unwatchedpages\"\n  | \"upload\"\n  | \"upload_by_url\"\n  | \"viewmywatchlist\"\n  | \"viewsuppressed\"\n  | \"writeapi\"\n\nexport interface WikimediaProfile extends Record<string, any> {\n  sub: string\n  username: string\n  editcount: number\n  confirmed_email: boolean\n  blocked: boolean\n  registered: string\n  groups: WikimediaGroup[]\n  rights: WikimediaRight[]\n  grants: WikimediaGrant[]\n  realname: string\n  email: string\n}\n\n/**\n * Add Wikimedia login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/wikimedia\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Wikimedia from \"@auth/core/providers/wikimedia\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Wikimedia({\n *       clientId: WIKIMEDIA_CLIENT_ID,\n *       clientSecret: WIKIMEDIA_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Wikimedia OAuth documentation](https://www.mediawiki.org/wiki/Extension:OAuth)\n *\n * ## Configuration steps\n * - Go to and accept the Consumer Registration doc: https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration\n * - Request a new OAuth 2.0 consumer to get the `clientId` and `clientSecret`: https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration/propose/oauth2\n *   - Add the following redirect URL into the console: `http://<your-next-app-url>/api/auth/callback/wikimedia`\n *   - Do not check the box next to This consumer is only for __your username__\n *   - Unless you explicitly need a larger scope, feel free to select the radio button labelled User identity verification only - no ability to read pages or act on the users behalf.\n *\n * After registration, you can initially test your application only with your own Wikimedia account.\n * You may have to wait several days for the application to be approved for it to be used by everyone.\n *\n * ### Notes\n * This provider also supports all Wikimedia projects:\n * - Wikipedia\n * - Wikidata\n * - Wikibooks\n * - Wiktionary\n * - etc..\n *\n * Please be aware that Wikimedia accounts do not have to have an associated email address. So you may want to add check if the user has an email address before allowing them to login.\n *\n * By default, Auth.js assumes that the Wikimedia provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Wikimedia provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/wikimedia.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Wikimedia<P extends WikimediaProfile>(\n  options: OAuthUserConfig<P>\n): OAuthConfig<P> {\n  return {\n    id: \"wikimedia\",\n    name: \"Wikimedia\",\n    type: \"oauth\",\n    token: \"https://meta.wikimedia.org/w/rest.php/oauth2/access_token\",\n    userinfo: \"https://meta.wikimedia.org/w/rest.php/oauth2/resource/profile\",\n    authorization:\n      \"https://meta.wikimedia.org/w/rest.php/oauth2/authorize?scope=\",\n    profile(profile) {\n      return {\n        id: profile.sub,\n        name: profile.username,\n        email: profile.email,\n        image: null,\n      }\n    },\n    style: { bg: \"#000\", text: \"#fff\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/wordpress.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>WordPress</b> integration.</span>\n * <a href=\"https://wordpress.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/wordpress.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/wordpress\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * Add WordPress login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/wordpress\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import WordPress from \"@auth/core/providers/wordpress\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     WordPress({\n *       clientId: WORKPRESS_CLIENT_ID,\n *       clientSecret: WORKPRESS_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [WordPress OAuth documentation](https://developer.wordpress.com/docs/oauth2/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the WordPress provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The WordPress provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/wordpress.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function WordPress(\n  config: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"wordpress\",\n    name: \"WordPress.com\",\n    type: \"oauth\",\n    authorization:\n      \"https://public-api.wordpress.com/oauth2/authorize?scope=auth\",\n    token: \"https://public-api.wordpress.com/oauth2/token\",\n    userinfo: \"https://public-api.wordpress.com/rest/v1/me\",\n    profile(profile) {\n      return {\n        id: profile.ID,\n        name: profile.display_name,\n        email: profile.email,\n        image: profile.avatar_URL,\n      }\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/workos.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>WorkOS</b> integration.</span>\n * <a href=\"https://workos.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/workos.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/workos\n */\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n/**\n * - {@link https://workos.com/docs/reference/sso/profile | The returned profile object}\n */\nexport interface WorkOSProfile extends Record<string, any> {\n  object: string\n  id: string\n  organization_id: string\n  connection_id: string\n  connection_type: string\n  idp_id: string\n  email: string\n  first_name: string\n  last_name: string\n  raw_attributes: {\n    id: string\n    email: string\n    lastName: string\n    firstName: string\n    picture: string\n  }\n}\n\n/**\n * Add WorkOS login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/workos\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import WorkOS from \"@auth/core/providers/workos\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     WorkOS({\n *       clientId: WORKOS_CLIENT_ID,\n *       clientSecret: WORKOS_CLIENT_SECRET,\n *       issuer: WORKOS_ISSUER,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [WorkOS SSO OAuth documentation](https://workos.com/docs/reference/sso)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the WorkOS provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * WorkOS is not an identity provider itself, but, rather, a bridge to multiple single sign-on (SSO) providers.\n * As a result, we need to make some additional changes to authenticate users using WorkOS.\n *\n * In order to sign a user in using WorkOS, we need to specify which WorkOS Connection to use.\n * A common way to do this is to collect the user's email address and extract the domain. This can be done using a custom login page.\n * To add a custom login page, you can use the `pages` option:\n * ```ts\n * pages: {\n *   signIn: \"/auth/signin\",\n * }\n * ```\n * We can then add a custom login page that displays an input where the user can enter their email address.\n * We then extract the domain from the user's email address and pass it to the `authorizationParams` parameter on the `signIn` function:\n * ```js title=\"pages/auth/signin.js\"\n * import { useState } from \"react\"\n * import { getProviders, signIn } from \"next-auth/react\"\n *\n * export default function SignIn({ providers }) {\n *   const [email, setEmail] = useState(\"\")\n *\n *   return (\n *     <>\n *       {Object.values(providers).map((provider) => {\n *         if (provider.id === \"workos\") {\n *           return (\n *             <div key={provider.id}>\n *               <input\n *                 type=\"email\"\n *                 value={email}\n *                 placeholder=\"Email\"\n *                 onChange={(event) => setEmail(event.target.value)}\n *               />\n *               <button\n *                 onClick={() =>\n *                   signIn(provider.id, undefined, {\n *                     domain: email.split(\"@\")[1],\n *                   })\n *                 }\n *               >\n *                 Sign in with SSO\n *               </button>\n *             </div>\n *           )\n *         }\n *\n *         return (\n *           <div key={provider.id}>\n *             <button onClick={() => signIn(provider.id)}>\n *               Sign in with {provider.name}\n *             </button>\n *           </div>\n *         )\n *       })}\n *     </>\n *   )\n * }\n *\n * export async function getServerSideProps(context) {\n *   const providers = await getProviders()\n *   return {\n *     props: { providers },\n *   }\n * }\n * ```\n *\n * :::tip\n *\n * The WorkOS provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/workos.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function WorkOS<P extends WorkOSProfile>(\n  options: OAuthUserConfig<P> & { connection?: string }\n): OAuthConfig<P> {\n  const { issuer = \"https://api.workos.com/\", connection = \"\" } = options\n\n  const connectionParams = new URLSearchParams({ connection })\n\n  return {\n    id: \"workos\",\n    name: \"WorkOS\",\n    type: \"oauth\",\n    authorization: `${issuer}sso/authorize?${connectionParams}`,\n    token: `${issuer}sso/token`,\n    client: {\n      token_endpoint_auth_method: \"client_secret_post\",\n    },\n    userinfo: `${issuer}sso/profile`,\n    profile(profile) {\n      return {\n        id: profile.id,\n        name: `${profile.first_name} ${profile.last_name}`,\n        email: profile.email,\n        image: profile.raw_attributes.picture ?? null,\n      }\n    },\n    style: { bg: \"#6363f1\", text: \"#fff\" },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/yandex.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#ffcc00\", display: \"flex\", justifyContent: \"space-between\", color: \"#000\", padding: 16}}>\n * <span>Built-in <b>Yandex</b> integration.</span>\n * <a href=\"https://yandex.com\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/yandex.svg\" height=\"48\" width=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/yandex\n */\n\nimport { OAuthConfig, OAuthUserConfig } from \"./oauth.js\"\n\n/**\n * - {@link https://yandex.com/dev/id/doc/en/user-information | Getting information about the user}\n * - {@link https://yandex.com/dev/id/doc/en/user-information#email-access | Access to email address}\n * - {@link https://yandex.com/dev/id/doc/en/user-information#avatar-access | Access to the user's profile picture}\n * - {@link https://yandex.com/dev/id/doc/en/user-information#birthday-access | Access to the date of birth}\n * - {@link https://yandex.com/dev/id/doc/en/user-information#name-access | Access to login, first name, last name, and gender}\n * - {@link https://yandex.com/dev/id/doc/en/user-information#phone-access | Access to the phone number}\n */\nexport interface YandexProfile {\n  /** User's Yandex login. */\n  login: string\n  /** Yandex user's unique ID. */\n  id: string\n  /**\n   * The ID of the app the OAuth token in the request was issued for.\n   * Available in the [app properties](https://oauth.yandex.com/). To open properties, click the app name.\n   */\n  client_id: string\n  /** Authorized Yandex user ID. It is formed on the Yandex side based on the `client_id` and `user_id` pair. */\n  psuid: string\n  /** An array of the user's email addresses. Currently only includes the default email address. */\n  emails?: string[]\n  /** The default email address for contacting the user. */\n  default_email?: string\n  /**\n   * Indicates that the stub (profile picture that is automatically assigned when registering in Yandex)\n   * ID is specified in the `default_avatar_id` field.\n   */\n  is_avatar_empty?: boolean\n  /**\n   * ID of the Yandex user's profile picture.\n   * Format for downloading user avatars: `https://avatars.yandex.net/get-yapic/<default_avatar_id>/<size>`\n   * @example \"https://avatars.yandex.net/get-yapic/31804/BYkogAC6AoB17bN1HKRFAyKiM4-1/islands-200\"\n   * Available sizes:\n   * `islands-small`: 28×28 pixels.\n   * `islands-34`: 34×34 pixels.\n   * `islands-middle`: 42×42 pixels.\n   * `islands-50`: 50×50 pixels.\n   * `islands-retina-small`: 56×56 pixels.\n   * `islands-68`: 68×68 pixels.\n   * `islands-75`: 75×75 pixels.\n   * `islands-retina-middle`: 84×84 pixels.\n   * `islands-retina-50`: 100×100 pixels.\n   * `islands-200`: 200×200 pixels.\n   */\n  default_avatar_id?: string\n  /**\n   * The user's date of birth in YYYY-MM-DD format.\n   * Unknown elements of the date are filled in with zeros, such as: `0000-12-23`.\n   * If the user's date of birth is unknow, birthday will be `null`\n   */\n  birthday?: string | null\n  first_name?: string\n  last_name?: string\n  display_name?: string\n  /**\n   * The first and last name that the user specified in Yandex ID.\n   * Non-Latin characters of the first and last names are presented in Unicode format.\n   */\n  real_name?: string\n  /** User's gender. `null` Stands for unknown or unspecified gender. Will be `undefined` if not provided by Yandex. */\n  sex?: \"male\" | \"female\" | null\n  /**\n   * The default phone number for contacting the user.\n   * The API can exclude the user's phone number from the response at its discretion.\n   * The field contains the following parameters:\n   * id: Phone number ID.\n   * number: The user's phone number.\n   */\n  default_phone?: { id: number; number: string }\n}\n\n/**\n * Add Yandex login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/yandex\n * ```\n *\n * #### Configuration\n * ```ts\n * import { Auth } from \"@auth/core\"\n * import Yandex from \"@auth/core/providers/yandex\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Yandex({ clientId: YANDEX_CLIENT_ID, clientSecret: YANDEX_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Yandex - Creating an OAuth app](https://yandex.com/dev/id/doc/en/register-client#create)\n * - [Yandex - Manage OAuth apps](https://oauth.yandex.com/)\n * - [Yandex - OAuth documentation](https://yandex.com/dev/id/doc/en/)\n * - [Learn more about OAuth](https://authjs.dev/concepts/oauth)\n * - [Source code](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/yandex.ts)\n *\n *:::tip\n * The Yandex provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/yandex.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n * :::\n *\n * :::info **Disclaimer**\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n * :::\n */\nexport default function Yandex(\n  options: OAuthUserConfig<YandexProfile>\n): OAuthConfig<YandexProfile> {\n  return {\n    id: \"yandex\",\n    name: \"Yandex\",\n    type: \"oauth\",\n    /** @see [Data access](https://yandex.com/dev/id/doc/en/register-client#access) */\n    authorization:\n      \"https://oauth.yandex.ru/authorize?scope=login:info+login:email+login:avatar\",\n    token: \"https://oauth.yandex.ru/token\",\n    userinfo: \"https://login.yandex.ru/info?format=json\",\n    profile(profile) {\n      return {\n        id: profile.id,\n        name: profile.display_name ?? profile.real_name ?? profile.first_name,\n        email: profile.default_email ?? profile.emails?.[0] ?? null,\n        image:\n          !profile.is_avatar_empty && profile.default_avatar_id\n            ? `https://avatars.yandex.net/get-yapic/${profile.default_avatar_id}/islands-200`\n            : null,\n      }\n    },\n    style: {\n      bg: \"#ffcc00\",\n      text: \"#000\",\n    },\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/zitadel.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Zitadel</b> integration.</span>\n * <a href=\"https://zitadel.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/zitadel.svg\" height=\"48\"/>\n * </a>\n * </div>\n *\n * @module providers/zitadel\n */\n\nimport type { OIDCConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * The returned user profile from ZITADEL when using the profile callback. See the standard claims reference [here](https://zitadel.com/docs/apis/openidoauth/claims#standard-claims).\n * If you need access to ZITADEL APIs or need additional information, make sure to add the corresponding scopes.\n */\nexport interface ZitadelProfile extends Record<string, any> {\n  amr: string // Authentication Method References as defined in RFC8176\n  aud: string // The audience of the token, by default all client id's and the project id are included\n  auth_time: number // UNIX time of the authentication\n  azp: string // Client id of the client who requested the token\n  email: string // Email Address of the subject\n  email_verified: boolean // if the email was verified by ZITADEL\n  exp: number // Time the token expires (as unix time)\n  family_name: string // The subjects family name\n  given_name: string // Given name of the subject\n  gender: string // Gender of the subject\n  iat: number // Time of the token was issued at (as unix time)\n  iss: string // Issuing domain of a token\n  jti: string // Unique id of the token\n  locale: string // Language from the subject\n  name: string // The subjects full name\n  nbf: number // Time the token must not be used before (as unix time)\n  picture: string // The subjects profile picture\n  phone: string // Phone number provided by the user\n  phone_verified: boolean // if the phonenumber was verified by ZITADEL\n  preferred_username: string // ZITADEL's login name of the user. Consist of username@primarydomain\n  sub: string // Subject ID of the user\n}\n\n/**\n * Add ZITADEL login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/zitadel\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import ZITADEL from \"@auth/core/providers/zitadel\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     ZITADEL({\n *       clientId: ZITADEL_CLIENT_ID,\n *       clientSecret: ZITADEL_CLIENT_SECRET,\n *     }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n * - [ZITADEL OpenID Endpoints](https://zitadel.com/docs/apis/openidoauth/endpoints)\n * - [ZITADEL recommended OAuth Flows](https://zitadel.com/docs/guides/integrate/oauth-recommended-flows)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the ZITADEL provider is\n * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.\n *\n * The Redirect URIs used when creating the credentials must include your full domain and end in the callback path. For example:\n * - For production: `https://{YOUR_DOMAIN}/api/auth/callback/zitadel`\n * - For development: `http://localhost:3000/api/auth/callback/zitadel`\n *\n * Make sure to enable dev mode in ZITADEL console to allow redirects for local development.\n *\n * :::tip\n *\n * The ZITADEL provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/zitadel.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n * :::tip\n *\n * ZITADEL also returns a email_verified boolean property in the profile. You can use this property to restrict access to people with verified accounts.\n * ```ts\n * const options = {\n *   ...\n *   callbacks: {\n *     async signIn({ account, profile }) {\n *       if (account.provider === \"zitadel\") {\n *         return profile.email_verified;\n *       }\n *       return true; // Do different verification for other providers that don't have `email_verified`\n *     },\n *   }\n *   ...\n * }\n * ```\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function ZITADEL<P extends ZitadelProfile>(\n  options: OAuthUserConfig<P>\n): OIDCConfig<P> {\n  return {\n    id: \"zitadel\",\n    name: \"ZITADEL\",\n    type: \"oidc\",\n    options,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/zoho.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>ZOHO</b> integration.</span>\n * <a href=\"https://zoho.com/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/zoho.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/zoho\n */\n\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n/**\n * Add ZOHO login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/zoho\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import ZOHO from \"@auth/core/providers/zoho\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     ZOHO({ clientId: ZOHO_CLIENT_ID, clientSecret: ZOHO_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Zoho OAuth 2.0 Integration Guide](https://www.zoho.com/accounts/protocol/oauth/web-server-applications.html)\n * - [Zoho API Console](https://api-console.zoho.com)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the ZOHO provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The ZOHO provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/zoho.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Zoho(\n  config: OAuthUserConfig<Record<string, any>>\n): OAuthConfig<Record<string, any>> {\n  return {\n    id: \"zoho\",\n    name: \"Zoho\",\n    type: \"oauth\",\n    authorization:\n      \"https://accounts.zoho.com/oauth/v2/auth?scope=AaaServer.profile.Read\",\n    token: \"https://accounts.zoho.com/oauth/v2/token\",\n    userinfo: \"https://accounts.zoho.com/oauth/user/info\",\n    profile(profile) {\n      return {\n        id: profile.ZUID,\n        name: `${profile.First_Name} ${profile.Last_Name}`,\n        email: profile.Email,\n        image: null,\n      }\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/providers/zoom.ts",
    "content": "/**\n * <div class=\"provider\" style={{backgroundColor: \"#000\", display: \"flex\", justifyContent: \"space-between\", color: \"#fff\", padding: 16}}>\n * <span>Built-in <b>Zoom</b> integration.</span>\n * <a href=\"https://zoom.us/\">\n *   <img style={{display: \"block\"}} src=\"https://authjs.dev/img/providers/zoom.svg\" height=\"48\" />\n * </a>\n * </div>\n *\n * @module providers/zoom\n */\n\nimport type { OAuthConfig, OAuthUserConfig } from \"./index.js\"\n\n/**\n * See: https://developers.zoom.us/docs/integrations/oauth/#using-an-access-token\n */\nexport interface ZoomProfile extends Record<string, any> {\n  id: string\n  first_name: string\n  last_name: string\n  email: string\n  type: number\n  role_name: string\n  pmi: number\n  use_pmi: boolean\n  vanity_url: string\n  personal_meeting_url: string\n  timezone: string\n  verified: number\n  dept: string\n  created_at: string\n  last_login_time: string\n  last_client_version: string\n  pic_url: string\n  host_key: string\n  jid: string\n  group_ids: string[]\n  im_group_ids: string[]\n  account_id: string\n  language: string\n  phone_country: string\n  phone_number: string\n  status: string\n}\n\n/**\n * Add Zoom login to your page.\n *\n * ### Setup\n *\n * #### Callback URL\n * ```\n * https://example.com/api/auth/callback/zoom\n * ```\n *\n * #### Configuration\n *```ts\n * import { Auth } from \"@auth/core\"\n * import Zoom from \"@auth/core/providers/zoom\"\n *\n * const request = new Request(origin)\n * const response = await Auth(request, {\n *   providers: [\n *     Zoom({ clientId: ZOOM_CLIENT_ID, clientSecret: ZOOM_CLIENT_SECRET }),\n *   ],\n * })\n * ```\n *\n * ### Resources\n *\n * - [Zoom OAuth 2.0 Integration Guide](https://developers.zoom.us/docs/integrations/oauth/)\n *\n * ### Notes\n *\n * By default, Auth.js assumes that the Zoom provider is\n * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.\n *\n * :::tip\n *\n * The Zoom provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/zoom.ts).\n * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).\n *\n * :::\n *\n * :::info **Disclaimer**\n *\n * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).\n *\n * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from\n * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,\n * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).\n *\n * :::\n */\nexport default function Zoom(\n  config: OAuthUserConfig<ZoomProfile>\n): OAuthConfig<ZoomProfile> {\n  return {\n    id: \"zoom\",\n    name: \"Zoom\",\n    type: \"oauth\",\n    authorization: \"https://zoom.us/oauth/authorize?scope\",\n    token: \"https://zoom.us/oauth/token\",\n    userinfo: \"https://api.zoom.us/v2/users/me\",\n    profile(profile) {\n      return {\n        id: profile.id,\n        name: `${profile.first_name} ${profile.last_name}`,\n        email: profile.email,\n        image: profile.pic_url,\n      }\n    },\n    style: {\n      bg: \"#0b5cff\",\n      text: \"#fff\",\n    },\n    options: config,\n  }\n}\n"
  },
  {
    "path": "packages/core/src/types.ts",
    "content": "/**\n *\n * This module contains public types and interfaces of the core package.\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/core\n * ```\n *\n * You can then import this submodule from `@auth/core/types`.\n *\n * ## Usage\n *\n * Even if you don't use TypeScript, IDEs like VS Code will pick up types to provide you with a better developer experience.\n * While you are typing, you will get suggestions about what certain objects/functions look like,\n * and sometimes links to documentation, examples, and other valuable resources.\n *\n * Generally, you will not need to import types from this module.\n * Mostly when using the `Auth` function and optionally the `AuthConfig` interface,\n * everything inside there will already be typed.\n *\n * :::tip\n * Inside the `Auth` function, you won't need to use a single type from this module.\n *\n * @example\n * ```ts title=index.ts\n * import { Auth } from \"@auth/core\"\n *\n * const request = new Request(\"https://example.com\")\n * const response = await Auth(request, {\n *   callbacks: {\n *     jwt(): JWT { // <-- This is unnecessary!\n *       return { foo: \"bar\" }\n *     },\n *     session(\n *        { session, token }: { session: Session; token: JWT } // <-- This is unnecessary!\n *     ) {\n *       return session\n *     },\n *   }\n * })\n * ```\n * :::\n *\n * ## Resources\n *\n * - [TypeScript - The Basics](https://www.typescriptlang.org/docs/handbook/2/basic-types.html)\n * - [Extending built-in types](https://authjs.dev/getting-started/typescript#module-augmentation)\n *\n * @module types\n */\n\nimport type { SerializeOptions } from \"./lib/vendored/cookie.js\"\nimport type { TokenEndpointResponse } from \"oauth4webapi\"\nimport type { Adapter } from \"./adapters.js\"\nimport { AuthConfig } from \"./index.js\"\nimport type { JWTOptions } from \"./jwt.js\"\nimport type { Cookie } from \"./lib/utils/cookie.js\"\nimport type { LoggerInstance } from \"./lib/utils/logger.js\"\nimport type { WarningCode } from \"./warnings.js\"\nimport type {\n  CredentialsConfig,\n  EmailConfig,\n  OAuthConfigInternal,\n  OIDCConfigInternal,\n  ProviderType,\n} from \"./providers/index.js\"\nimport type {\n  WebAuthnConfig,\n  WebAuthnProviderType,\n} from \"./providers/webauthn.js\"\n\nexport type { WebAuthnOptionsResponseBody } from \"./lib/utils/webauthn-utils.js\"\nexport type { AuthConfig } from \"./index.js\"\nexport type { LoggerInstance, WarningCode }\nexport type Awaitable<T> = T | PromiseLike<T>\nexport type Awaited<T> = T extends Promise<infer U> ? U : T\n\nexport type SemverString =\n  | `v${number}`\n  | `v${number}.${number}`\n  | `v${number}.${number}.${number}`\n\n/**\n * Change the theme of the built-in pages.\n *\n * [Documentation](https://authjs.dev/reference/core#theme) |\n * [Pages](https://authjs.dev/guides/pages/signin)\n */\nexport interface Theme {\n  colorScheme?: \"auto\" | \"dark\" | \"light\"\n  logo?: string\n  brandColor?: string\n  buttonText?: string\n}\n\n/**\n * Different tokens returned by OAuth Providers.\n * Some of them are available with different casing,\n * but they refer to the same value.\n */\nexport type TokenSet = Partial<TokenEndpointResponse> & {\n  /**\n   * Date of when the `access_token` expires in seconds.\n   * This value is calculated from the `expires_in` value.\n   *\n   * @see https://www.ietf.org/rfc/rfc6749.html#section-4.2.2\n   */\n  expires_at?: number\n}\n\n/**\n * Usually contains information about the provider being used\n * and also extends `TokenSet`, which is different tokens returned by OAuth Providers.\n */\nexport interface Account extends Partial<TokenEndpointResponse> {\n  /** Provider's id for this account. E.g. \"google\". See the full list at https://authjs.dev/reference/core/providers */\n  provider: string\n  /**\n   * This value depends on the type of the provider being used to create the account.\n   * - oauth/oidc: The OAuth account's id, returned from the `profile()` callback.\n   * - email: The user's email address.\n   * - credentials: `id` returned from the `authorize()` callback\n   */\n  providerAccountId: string\n  /** Provider's type for this account */\n  type: ProviderType\n  /**\n   * id of the user this account belongs to\n   *\n   * @see https://authjs.dev/reference/core/adapters#adapteruser\n   */\n  userId?: string\n  /**\n   * Calculated value based on {@link TokenEndpointResponse.expires_in}.\n   *\n   * It is the absolute timestamp (in seconds) when the {@link TokenEndpointResponse.access_token} expires.\n   *\n   * This value can be used for implementing token rotation together with {@link TokenEndpointResponse.refresh_token}.\n   *\n   * @see https://authjs.dev/guides/refresh-token-rotation#database-strategy\n   * @see https://www.rfc-editor.org/rfc/rfc6749#section-5.1\n   */\n  expires_at?: number\n}\n\n/**\n * The user info returned from your OAuth provider.\n *\n * @see https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims\n */\nexport interface Profile {\n  id?: string | null\n  sub?: string | null\n  name?: string | null\n  given_name?: string | null\n  family_name?: string | null\n  middle_name?: string | null\n  nickname?: string | null\n  preferred_username?: string | null\n  profile?: string | null\n  picture?: string | null | any\n  website?: string | null\n  email?: string | null\n  email_verified?: boolean | null\n  gender?: string | null\n  birthdate?: string | null\n  zoneinfo?: string | null\n  locale?: string | null\n  phone_number?: string | null\n  updated_at?: Date | string | number | null\n  address?: {\n    formatted?: string | null\n    street_address?: string | null\n    locality?: string | null\n    region?: string | null\n    postal_code?: string | null\n    country?: string | null\n  } | null\n  [claim: string]: unknown\n}\n\n/** [Documentation](https://authjs.dev/reference/core#cookies) */\nexport interface CookieOption {\n  name: string\n  options: SerializeOptions\n}\n\n/** [Documentation](https://authjs.dev/reference/core#cookies) */\nexport interface CookiesOptions {\n  sessionToken: Partial<CookieOption>\n  callbackUrl: Partial<CookieOption>\n  csrfToken: Partial<CookieOption>\n  pkceCodeVerifier: Partial<CookieOption>\n  state: Partial<CookieOption>\n  nonce: Partial<CookieOption>\n  webauthnChallenge: Partial<CookieOption>\n}\n\n/** TODO: Check if all these are used/correct */\nexport type ErrorPageParam = \"Configuration\" | \"AccessDenied\" | \"Verification\"\n\n/** TODO: Check if all these are used/correct */\nexport type SignInPageErrorParam =\n  | \"Signin\"\n  | \"OAuthSignin\"\n  | \"OAuthCallbackError\"\n  | \"OAuthCreateAccount\"\n  | \"EmailCreateAccount\"\n  | \"Callback\"\n  | \"OAuthAccountNotLinked\"\n  | \"EmailSignin\"\n  | \"CredentialsSignin\"\n  | \"SessionRequired\"\n\nexport interface PagesOptions {\n  /**\n   * The path to the sign in page.\n   *\n   * The optional \"error\" query parameter is set to\n   * one of the {@link SignInPageErrorParam available} values.\n   *\n   * @default \"/signin\"\n   */\n  signIn: string\n  signOut: string\n  /**\n   * The path to the error page.\n   *\n   * The optional \"error\" query parameter is set to\n   * one of the {@link ErrorPageParam available} values.\n   *\n   * @default \"/error\"\n   */\n  error: string\n  verifyRequest: string\n  /** If set, new users will be directed here on first sign in */\n  newUser: string\n}\n\ntype ISODateString = string\n\nexport interface DefaultSession {\n  user?: User\n  expires: ISODateString\n}\n\n/** The active session of the logged in user. */\nexport interface Session extends DefaultSession {}\n\nexport interface DefaultUser {\n  id?: string\n  name?: string | null\n  email?: string | null\n  image?: string | null\n}\n\n/**\n * The shape of the returned object in the OAuth providers' `profile` callback,\n * available in the `jwt` and `session` callbacks,\n * or the second parameter of the `session` callback, when using a database.\n */\nexport interface User extends DefaultUser {}\n\n// Below are types that are only supposed be used by next-auth internally\n\n/** @internal */\nexport type InternalProvider<T = ProviderType> = (T extends \"oauth\"\n  ? OAuthConfigInternal<any>\n  : T extends \"oidc\"\n    ? OIDCConfigInternal<any>\n    : T extends \"email\"\n      ? EmailConfig\n      : T extends \"credentials\"\n        ? CredentialsConfig\n        : T extends WebAuthnProviderType\n          ? WebAuthnConfig\n          : never) & {\n  signinUrl: string\n  /** @example `\"https://example.com/api/auth/callback/id\"` */\n  callbackUrl: string\n}\n\nexport interface PublicProvider {\n  id: string\n  name: string\n  type: string\n  signinUrl: string\n  callbackUrl: string\n}\n\n/**\n * Supported actions by Auth.js. Each action map to a REST API endpoint.\n * Some actions have a `GET` and `POST` variant, depending on if the action\n * changes the state of the server.\n *\n * - **`\"callback\"`**:\n *   - **`GET`**: Handles the callback from an [OAuth provider](https://authjs.dev/reference/core/providers#oauth2configprofile).\n *   - **`POST`**: Handles the callback from a [Credentials provider](https://authjs.dev/getting-started/providers/credentials#credentialsconfigcredentialsinputs).\n * - **`\"csrf\"`**: Returns the raw CSRF token, which is saved in a cookie (encrypted).\n * It is used for CSRF protection, implementing the [double submit cookie](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie) technique.\n * :::note\n * Some frameworks have built-in CSRF protection and can therefore disable this action. In this case, the corresponding endpoint will return a 404 response. Read more at [`skipCSRFCheck`](https://authjs.dev/reference/core#skipcsrfcheck).\n * _⚠ We don't recommend manually disabling CSRF protection, unless you know what you're doing._\n * :::\n * - **`\"error\"`**: Renders the built-in error page.\n * - **`\"providers\"`**: Returns a client-safe list of all configured providers.\n * - **`\"session\"`**:\n *   - **`GET`**: Returns the user's session if it exists, otherwise `null`.\n *   - **`POST`**: Updates the user's session and returns the updated session.\n * - **`\"signin\"`**:\n *   - **`GET`**: Renders the built-in sign-in page.\n *   - **`POST`**: Initiates the sign-in flow.\n * - **`\"signout\"`**:\n *   - **`GET`**: Renders the built-in sign-out page.\n *   - **`POST`**: Initiates the sign-out flow. This will invalidate the user's session (deleting the cookie, and if there is a session in the database, it will be deleted as well).\n * - **`\"verify-request\"`**: Renders the built-in verification request page.\n * - **`\"webauthn-options\"`**:\n *   - **`GET`**: Returns the options for the WebAuthn authentication and registration flows.\n */\nexport type AuthAction =\n  | \"callback\"\n  | \"csrf\"\n  | \"error\"\n  | \"providers\"\n  | \"session\"\n  | \"signin\"\n  | \"signout\"\n  | \"verify-request\"\n  | \"webauthn-options\"\n\n/** @internal */\nexport interface RequestInternal {\n  url: URL\n  method: \"GET\" | \"POST\"\n  cookies?: Partial<Record<string, string>>\n  headers?: Record<string, any>\n  query?: Record<string, any>\n  body?: Record<string, any>\n  action: AuthAction\n  providerId?: string\n  error?: string\n}\n\n// Should only be used by frameworks\nexport interface ResponseInternal<\n  Body extends string | Record<string, any> | any[] | null = any,\n> {\n  status?: number\n  headers?: Headers | HeadersInit\n  body?: Body\n  redirect?: string\n  cookies?: Cookie[]\n}\n\n/**\n * A webauthn authenticator.\n * Represents an entity capable of authenticating the account it references,\n * and contains the auhtenticator's credentials and related information.\n *\n * @see https://www.w3.org/TR/webauthn/#authenticator\n */\nexport interface Authenticator {\n  /**\n   * ID of the user this authenticator belongs to.\n   */\n  userId?: string\n  /**\n   * The provider account ID connected to the authenticator.\n   */\n  providerAccountId: string\n  /**\n   * Number of times the authenticator has been used.\n   */\n  counter: number\n  /**\n   * Whether the client authenticator backed up the credential.\n   */\n  credentialBackedUp: boolean\n  /**\n   * Base64 encoded credential ID.\n   */\n  credentialID: string\n  /**\n   * Base64 encoded credential public key.\n   */\n  credentialPublicKey: string\n  /**\n   * Concatenated transport flags.\n   */\n  transports?: string | null\n  /**\n   * Device type of the authenticator.\n   */\n  credentialDeviceType: string\n}\n\n/** @internal */\nexport interface InternalOptions<TProviderType = ProviderType> {\n  providers: InternalProvider[]\n  url: URL\n  action: AuthAction\n  provider: InternalProvider<TProviderType>\n  csrfToken?: string\n  /**\n   * `true` if the [Double-submit CSRF check](https://owasp.org/www-chapter-london/assets/slides/David_Johansson-Double_Defeat_of_Double-Submit_Cookie.pdf) was successful\n   * or [`skipCSRFCheck`](https://authjs.dev/reference/core#skipcsrfcheck) was enabled.\n   */\n  csrfTokenVerified?: boolean\n  secret: string | string[]\n  theme: Theme\n  debug: boolean\n  logger: LoggerInstance\n  session: NonNullable<Required<AuthConfig[\"session\"]>>\n  pages: Partial<PagesOptions>\n  jwt: JWTOptions\n  events: NonNullable<AuthConfig[\"events\"]>\n  adapter: Required<Adapter> | undefined\n  callbacks: NonNullable<Required<AuthConfig[\"callbacks\"]>>\n  cookies: Record<keyof CookiesOptions, CookieOption>\n  callbackUrl: string\n  /**\n   * If true, the OAuth callback is being proxied by the server to the original URL.\n   * See also {@link OAuthConfigInternal.redirectProxyUrl}.\n   */\n  isOnRedirectProxy: boolean\n  experimental: NonNullable<AuthConfig[\"experimental\"]>\n  basePath: string\n}\n"
  },
  {
    "path": "packages/core/src/warnings.ts",
    "content": "/**\n * - `debug-enabled`: The `debug` option was evaluated to `true`. It adds extra logs in the terminal which is useful in development,\n *   but since it can print sensitive information about users, make sure to set this to `false` in production.\n *   In Node.js environments, you can for example set `debug: process.env.NODE_ENV !== \"production\"`.\n *   Consult with your runtime/framework on how to set this value correctly.\n * - `csrf-disabled`: You were trying to get a CSRF response from Auth.js (eg.: by calling a `/csrf` endpoint),\n *   but in this setup, CSRF protection via Auth.js was turned off. This is likely if you are not directly using `@auth/core`\n *   but a framework library (like `@auth/sveltekit`) that already has CSRF protection built-in. You likely won't need the CSRF response.\n * - `env-url-basepath-redundant`: `AUTH_URL` (or `NEXTAUTH_URL`) and `authConfig.basePath` are both declared. This is a configuration mistake - you should either remove the `authConfig.basePath` configuration,\n *   or remove the `pathname` of `AUTH_URL` (or `NEXTAUTH_URL`). Only one of them is needed.\n * - `env-url-basepath-mismatch`: `AUTH_URL` (or `NEXTAUTH_URL`) and `authConfig.basePath` are both declared, but they don't match. This is a configuration mistake.\n *   `@auth/core` will use `basePath` to construct the full URL to the corresponding action (/signin, /signout, etc.) in this case.\n * - `experimental-webauthn`: Experimental WebAuthn feature is enabled.\n *\n */\nexport type WarningCode =\n  | \"debug-enabled\"\n  | \"csrf-disabled\"\n  | \"env-url-basepath-redundant\"\n  | \"env-url-basepath-mismatch\"\n  | \"experimental-webauthn\"\n"
  },
  {
    "path": "packages/core/test/actions/callback.test.ts",
    "content": "import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\"\n\nimport GitHub from \"../../src/providers/github.js\"\nimport Credentials from \"../../src/providers/credentials.js\"\n\nimport { logger, makeAuthRequest, testConfig } from \"../utils.js\"\nimport { skipCSRFCheck } from \"../../src/index.js\"\nimport { CredentialsSignin, InvalidCheck } from \"../../src/errors.js\"\nimport { state } from \"../../src/lib/actions/callback/oauth/checks.js\"\n\ndescribe(\"assert GET callback action\", () => {\n  beforeEach(() => {\n    vi.resetAllMocks()\n  })\n  afterEach(() => {\n    vi.restoreAllMocks()\n  })\n  it(\"should throw Configuration error if the provider ID is not found\", async () => {\n    const { response } = await makeAuthRequest({\n      action: \"callback\",\n      path: \"/invalid-provider\",\n    })\n\n    expect(response.status).toEqual(302)\n    expect(response.headers.get(\"location\")).toEqual(\n      `https://authjs.test/auth/error?error=Configuration`\n    )\n  })\n\n  describe(\"Redirect Proxy\", () => {\n    it(\"should throw Configuration error if missing query state and isOnRedirectProxy is true\", async () => {\n      const { response } = await makeAuthRequest({\n        action: \"callback\",\n        path: \"/github\",\n        query: { state: \"random\" },\n        config: {\n          redirectProxyUrl: \"https://login.example.com\",\n          providers: [GitHub],\n        },\n      })\n\n      expect(response.status).toEqual(302)\n      expect(response.headers.get(\"location\")).toEqual(\n        `https://authjs.test/auth/error?error=Configuration`\n      )\n    })\n\n    it(\"should redirect query with state if redirectProxyUrl is defined, and state query contains origin\", async () => {\n      const proxyURL = \"https://login.example.com/auth\"\n      const originURL = \"https://origin.example.com\"\n\n      const config = testConfig({\n        debug: true,\n        redirectProxyUrl: proxyURL,\n        providers: [GitHub],\n      })\n\n      const stateCookie = (await state.create(\n        {\n          logger: config.logger,\n          provider: { checks: [\"state\"] },\n          jwt: { secret: config.secret },\n          cookies: {\n            state: {\n              name: \"authjs.state\",\n              options: { secure: true, sameSite: \"lax\", httpOnly: true },\n            },\n          },\n        } as any,\n        originURL\n      )) as any\n\n      const params = { state: stateCookie?.value, code: \"random\" }\n      const { response } = await makeAuthRequest({\n        action: \"callback\",\n        path: \"/github\",\n        query: params,\n        host: \"login.example.com\",\n        config,\n      })\n\n      expect(response.status).toEqual(302)\n      expect(response.headers.get(\"location\")).toEqual(\n        `${originURL}?${new URLSearchParams(params)}`\n      )\n    })\n\n    it(\"should error if secret does not match and state cannot be decoded\", async () => {\n      const proxyURL = \"https://login.example.com/auth\"\n      const originURL = \"https://origin.example.com\"\n\n      const config = testConfig({\n        logger,\n        debug: true,\n        redirectProxyUrl: proxyURL,\n        providers: [GitHub],\n      })\n\n      const stateCookie = (await state.create(\n        {\n          logger: config.logger,\n          provider: { checks: [\"state\"] },\n          jwt: { secret: \"something random\" },\n          cookies: {\n            state: {\n              name: \"authjs.state\",\n              options: { secure: true, sameSite: \"lax\", httpOnly: true },\n            },\n          },\n        } as any,\n        originURL\n      )) as any\n\n      const params = { state: stateCookie?.value, code: \"random\" }\n      const { response } = await makeAuthRequest({\n        action: \"callback\",\n        path: \"/github\",\n        query: params,\n        host: \"login.example.com\",\n        config,\n      })\n\n      expect(response.status).toEqual(302)\n      expect(response.headers.get(\"location\")).toEqual(\n        `${proxyURL}/error?${new URLSearchParams({ error: \"Configuration\" })}`\n      )\n      expect(logger.error).toHaveBeenCalledWith(\n        new InvalidCheck(\"State could not be decoded\")\n      )\n    })\n  })\n\n  it(\"should redirect to the custom error page is custom error page is defined\", async () => {\n    const { response } = await makeAuthRequest({\n      action: \"callback\",\n      path: \"/credentials\",\n      config: {\n        pages: {\n          error: \"/custom/error\",\n        },\n        providers: [Credentials],\n      },\n    })\n\n    expect(response.status).toEqual(302)\n    expect(response.headers.get(\"location\")).toEqual(\n      `https://authjs.test/custom/error?error=Configuration`\n    )\n  })\n})\n\ndescribe(\"assert POST callback action\", () => {\n  describe(\"Credentials provider\", () => {\n    it(\"should return error=CredentialSignin and code=credentials if authorize returns null\", async () => {\n      const { response } = await makeAuthRequest({\n        action: \"callback\",\n        path: \"/credentials\",\n        body: {\n          username: \"foo\",\n          password: \"bar\",\n        },\n        config: {\n          skipCSRFCheck,\n          providers: [\n            Credentials({\n              authorize() {\n                return null\n              },\n            }),\n          ],\n        },\n      })\n\n      expect(response.status).toEqual(302)\n      expect(response.headers.get(\"location\")).toEqual(\n        `https://authjs.test/auth/signin?error=CredentialsSignin&code=credentials`\n      )\n    })\n\n    it(\"should return error=Configuration if authorize throws an Error that is not extending from CredentialSignin\", async () => {\n      const { response } = await makeAuthRequest({\n        action: \"callback\",\n        path: \"/credentials\",\n        body: {\n          username: \"foo\",\n          password: \"bar\",\n        },\n        config: {\n          skipCSRFCheck,\n          providers: [\n            Credentials({\n              authorize() {\n                throw new Error()\n              },\n            }),\n          ],\n        },\n      })\n\n      expect(response.status).toEqual(302)\n      expect(response.headers.get(\"location\")).toEqual(\n        `https://authjs.test/auth/error?error=Configuration`\n      )\n    })\n\n    it(\"should return error=CredentialsSignin and code=custom if authorize throws an custom Error that is extending from CredentialSignin\", async () => {\n      class CustomSigninError extends CredentialsSignin {\n        code = \"custom\"\n      }\n      const { response } = await makeAuthRequest({\n        action: \"callback\",\n        path: \"/credentials\",\n        body: {\n          username: \"foo\",\n          password: \"bar\",\n        },\n        config: {\n          skipCSRFCheck,\n          providers: [\n            Credentials({\n              authorize() {\n                throw new CustomSigninError()\n              },\n            }),\n          ],\n        },\n      })\n\n      expect(response.status).toEqual(302)\n      expect(response.headers.get(\"location\")).toEqual(\n        `https://authjs.test/auth/signin?error=CredentialsSignin&code=custom`\n      )\n    })\n  })\n})\n"
  },
  {
    "path": "packages/core/test/actions/csrf.test.ts",
    "content": "import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\"\n\nimport {\n  makeAuthRequest,\n  testConfig,\n  assertNoCacheResponseHeaders,\n} from \"../utils.js\"\n\ndescribe(\"assert GET CSRF action\", () => {\n  beforeEach(() => {\n    vi.resetAllMocks()\n  })\n  afterEach(() => {\n    vi.restoreAllMocks()\n  })\n  it(\"shoud return CSRF token with no cache headers\", async () => {\n    const authConfig = testConfig()\n    const { response } = await makeAuthRequest({\n      action: \"csrf\",\n      config: authConfig,\n    })\n    assertNoCacheResponseHeaders(response)\n    const body = await response.json()\n\n    expect(body.csrfToken).toBeDefined()\n  })\n})\n"
  },
  {
    "path": "packages/core/test/actions/session.test.ts",
    "content": "import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\"\n\nimport * as cookie from \"../../src/lib/vendored/cookie.js\"\nimport { MemoryAdapter, initMemory } from \"../memory-adapter.js\"\nimport { randomString } from \"../../src/lib/utils/web.js\"\nimport type { AdapterUser } from \"../../src/adapters.js\"\nimport { decode, encode } from \"../../src/jwt.js\"\nimport {\n  callbacks,\n  getExpires,\n  events,\n  logger,\n  makeAuthRequest,\n  testConfig,\n  AUTH_SECRET,\n  SESSION_COOKIE_NAME,\n  assertNoCacheResponseHeaders,\n} from \"../utils.js\"\n\nconst { parse: parseCookie } = cookie\n\ndescribe(\"assert GET session action\", () => {\n  beforeEach(() => {\n    vi.resetAllMocks()\n  })\n  afterEach(() => {\n    vi.restoreAllMocks()\n  })\n  describe(\"JWT strategy\", () => {\n    it(\"should return a valid JWT session response\", async () => {\n      const authConfig = testConfig()\n\n      const expectedExpires = getExpires()\n\n      vi.spyOn(callbacks, \"jwt\")\n      vi.spyOn(callbacks, \"session\")\n\n      const expectedUser = {\n        name: \"test\",\n        email: \"test@test.com\",\n        picture: \"https://test.com/test.png\",\n      }\n      const expectedUserInBody = {\n        name: \"test\",\n        email: \"test@test.com\",\n        image: \"https://test.com/test.png\",\n      }\n      const expectedToken = {\n        ...expectedUser,\n        exp: expect.any(Number),\n        iat: expect.any(Number),\n        jti: expect.any(String),\n      }\n      const { response } = await makeAuthRequest({\n        action: \"session\",\n        cookies: {\n          [SESSION_COOKIE_NAME]: await encode({\n            salt: SESSION_COOKIE_NAME,\n            secret: AUTH_SECRET,\n            token: expectedUser,\n          }),\n        },\n        config: authConfig,\n      })\n      const actualBodySession = await response.json()\n\n      let cookies = response.headers.getSetCookie().reduce((acc, cookie) => {\n        return { ...acc, ...parseCookie(cookie) }\n      }, {})\n      const sessionToken = cookies[SESSION_COOKIE_NAME]\n      const actualToken = await decode({\n        salt: SESSION_COOKIE_NAME,\n        secret: AUTH_SECRET,\n        token: sessionToken,\n      })\n\n      const { exp, iat, jti, ...actualUser } = actualToken || {}\n\n      const expectedSession = {\n        user: expectedUserInBody,\n        expires: expectedExpires.toISOString(),\n      }\n\n      expect(actualUser).toEqual(expectedUser)\n      expect(actualBodySession).toEqual(expectedSession)\n      expect(authConfig.events?.session).toHaveBeenCalledWith({\n        session: expectedSession,\n        token: expectedToken,\n      })\n      expect(callbacks.jwt).toHaveBeenCalledWith({\n        token: expectedToken,\n        session: undefined,\n      })\n      expect(callbacks.session).toHaveBeenCalledWith({\n        session: expectedSession,\n        token: expectedToken,\n      })\n\n      assertNoCacheResponseHeaders(response)\n    })\n\n    it(\"should return null if no JWT session in the requests cookies\", async () => {\n      const { response } = await makeAuthRequest({\n        action: \"session\",\n      })\n      const actual = await response.json()\n      expect(actual).toEqual(null)\n\n      assertNoCacheResponseHeaders(response)\n    })\n\n    it(\"should return null if JWT session is invalid\", async () => {\n      const { response } = await makeAuthRequest({\n        action: \"session\",\n        cookies: {\n          [SESSION_COOKIE_NAME]: \"invalid\",\n        },\n      })\n      const actual = await response.json()\n      expect(actual).toEqual(null)\n\n      assertNoCacheResponseHeaders(response)\n    })\n\n    it(\"should throw invalid JWT error if salt is invalid\", async () => {\n      const { response } = await makeAuthRequest({\n        action: \"session\",\n        cookies: {\n          [SESSION_COOKIE_NAME]: await encode({\n            salt: \"__Secure-authjs.session-token-invalid\",\n            secret: AUTH_SECRET,\n            token: {\n              name: \"test\",\n              email: \"test@test.com\",\n              picture: \"https://test.com/test.png\",\n            },\n          }),\n        },\n      })\n      const actual = await response.json()\n\n      expect(actual).toEqual(null)\n      expect(logger.error).toHaveBeenCalledOnce()\n\n      assertNoCacheResponseHeaders(response)\n    })\n  })\n  describe(\"Database strategy\", () => {\n    it(\"should return a valid database session in the response, and update the session in the database\", async () => {\n      vi.spyOn(callbacks, \"jwt\")\n      vi.spyOn(callbacks, \"session\")\n      const updatedExpires = getExpires()\n      const currentExpires = getExpires(24 * 60 * 60 * 1000) // 1 day from now\n\n      const expectedSessionToken = randomString(32)\n      const expectedUserId = randomString(32)\n      const expectedUser = {\n        id: expectedUserId,\n        name: \"test\",\n        email: \"test@test.com\",\n        image: \"https://test.com/test.png\",\n        emailVerified: null,\n      } satisfies AdapterUser\n\n      // const expectedUserId = randomString(32)\n      const memory = initMemory()\n      memory.users.set(expectedUserId, expectedUser)\n      memory.sessions.set(expectedSessionToken, {\n        sessionToken: expectedSessionToken,\n        userId: expectedUserId,\n        expires: currentExpires,\n      })\n\n      const adapter = MemoryAdapter(memory)\n\n      const { response } = await makeAuthRequest({\n        action: \"session\",\n        cookies: {\n          [SESSION_COOKIE_NAME]: expectedSessionToken,\n        },\n        config: {\n          adapter,\n        },\n      })\n\n      const actualBodySession = await response.json()\n\n      let cookies = response.headers.getSetCookie().reduce((acc, cookie) => {\n        return { ...acc, ...parseCookie(cookie) }\n      }, {})\n      const actualSessionToken = cookies[SESSION_COOKIE_NAME]\n\n      expect(memory.users.get(expectedUserId)).toEqual(expectedUser)\n      expect(memory.sessions.get(expectedSessionToken)).toEqual({\n        sessionToken: expectedSessionToken,\n        userId: expectedUserId,\n        expires: updatedExpires,\n      })\n\n      expect(callbacks.session).toHaveBeenCalledWith({\n        newSession: undefined,\n        session: {\n          user: expectedUser,\n          expires: currentExpires,\n          sessionToken: expectedSessionToken,\n          userId: expectedUserId,\n        },\n        user: expectedUser,\n      })\n      expect(callbacks.jwt).not.toHaveBeenCalled()\n\n      expect(actualSessionToken).toEqual(expectedSessionToken)\n      expect(actualBodySession.user).toEqual({\n        image: expectedUser.image,\n        name: expectedUser.name,\n        email: expectedUser.email,\n      })\n      expect(actualBodySession.expires).toEqual(currentExpires.toISOString())\n\n      assertNoCacheResponseHeaders(response)\n    })\n\n    it(\"should return null in the response, and delete the session\", async () => {\n      vi.spyOn(callbacks, \"jwt\")\n      vi.spyOn(callbacks, \"session\")\n      const currentExpires = getExpires(-24 * 60 * 60 * 1000) // 1 day before\n\n      const expectedSessionToken = randomString(32)\n      const expectedUserId = randomString(32)\n      const expectedUser = {\n        id: expectedUserId,\n        name: \"test\",\n        email: \"test@test.com\",\n        image: \"https://test.com/test.png\",\n        emailVerified: null,\n      } satisfies AdapterUser\n\n      const memory = initMemory()\n      memory.users.set(expectedUserId, expectedUser)\n      memory.sessions.set(expectedSessionToken, {\n        sessionToken: expectedSessionToken,\n        userId: expectedUserId,\n        expires: currentExpires,\n      })\n\n      const adapter = MemoryAdapter(memory)\n\n      const { response } = await makeAuthRequest({\n        action: \"session\",\n        cookies: {\n          [SESSION_COOKIE_NAME]: expectedSessionToken,\n        },\n        config: {\n          adapter,\n        },\n      })\n\n      const actualBodySession = await response.json()\n\n      let cookies = response.headers.getSetCookie().reduce((acc, cookie) => {\n        return { ...acc, ...parseCookie(cookie) }\n      }, {})\n      const actualSessionToken = cookies[SESSION_COOKIE_NAME]\n\n      expect(memory.users.get(expectedUserId)).toEqual(expectedUser)\n      expect(memory.sessions.get(expectedSessionToken)).toEqual(undefined)\n      expect(callbacks.session).not.toHaveBeenCalled()\n      expect(events.session).not.toHaveBeenCalled()\n      expect(callbacks.jwt).not.toHaveBeenCalled()\n\n      expect(actualSessionToken).toEqual(\"\")\n      expect(actualBodySession).toEqual(null)\n\n      assertNoCacheResponseHeaders(response)\n    })\n  })\n})\n"
  },
  {
    "path": "packages/core/test/assert-config.test.ts",
    "content": "import { vi, expect, it, describe, beforeEach } from \"vitest\"\nimport { makeAuthRequest } from \"./utils\"\nimport {\n  InvalidCallbackUrl,\n  InvalidEndpoints,\n  MissingAuthorize,\n  MissingSecret,\n  UnsupportedStrategy,\n  UntrustedHost,\n} from \"../src/errors\"\nimport { Provider } from \"../src/providers\"\nimport { AuthConfig } from \"../src\"\nimport Credentials from \"../src/providers/credentials\"\n\ndescribe(\"Assert user config correctness\", () => {\n  beforeEach(() => {\n    vi.resetAllMocks()\n  })\n\n  it(\"missing secret\", async () => {\n    const { response, logger } = await makeAuthRequest({\n      action: \"session\",\n      config: { secret: undefined },\n    })\n\n    expect(response.status).toBe(500)\n    expect(await response.json()).toEqual({\n      message:\n        \"There was a problem with the server configuration. Check the server logs for more information.\",\n    })\n    expect(logger?.error).toHaveBeenCalledWith(\n      new MissingSecret(\"Please define a `secret`\")\n    )\n  })\n\n  it(\"trust host\", async () => {\n    const { response, logger } = await makeAuthRequest({\n      action: \"session\",\n      config: { trustHost: false },\n    })\n\n    expect(response.status).toBe(500)\n    expect(await response.json()).toEqual({\n      message:\n        \"There was a problem with the server configuration. Check the server logs for more information.\",\n    })\n    expect(logger?.error).toHaveBeenCalledWith(\n      new UntrustedHost(\n        \"Host must be trusted. URL was: https://authjs.test/auth/session\"\n      )\n    )\n  })\n\n  describe.each([\"non-relative\", \"a\"])(\n    'invalid callbackUrl (url: \"%s\")',\n    async (callbackUrl) => {\n      it.each([\"search param\", \"cookie\", \"cookie-custom\"])(\n        \"from %s\",\n        async (type) => {\n          const cookieName =\n            type === \"cookie\" ? \"__Secure-authjs.callback-url\" : \"custom-name\"\n\n          const { response, logger } = await makeAuthRequest({\n            action: \"session\",\n            ...(type.startsWith(\"cookie\")\n              ? { cookies: { [cookieName]: callbackUrl } }\n              : { query: { callbackUrl } }),\n            config: {\n              cookies:\n                type === \"cookie-custom\"\n                  ? { callbackUrl: { name: cookieName } }\n                  : undefined,\n            },\n          })\n\n          expect(response.status).toBe(500)\n          expect(await response.json()).toEqual({\n            message:\n              \"There was a problem with the server configuration. Check the server logs for more information.\",\n          })\n          expect(logger?.error).toHaveBeenCalledWith(\n            new InvalidCallbackUrl(\n              `Invalid callback URL. Received: ${callbackUrl}`\n            )\n          )\n        }\n      )\n    }\n  )\n\n  describe(\"providers\", () => {\n    it.each<[Provider, string]>([\n      [\n        () => ({ type: \"oidc\", id: \"provider-id\", name: \"\" }),\n        'Provider \"provider-id\" is missing both `issuer` and `authorization` endpoint config. At least one of them is required',\n      ],\n      [\n        { type: \"oidc\", id: \"provider-id\", name: \"\" },\n        'Provider \"provider-id\" is missing both `issuer` and `authorization` endpoint config. At least one of them is required',\n      ],\n      [\n        {\n          authorization: { url: \"http://a\" },\n          type: \"oauth\",\n          id: \"provider-id\",\n          name: \"\",\n        },\n        'Provider \"provider-id\" is missing both `issuer` and `token` endpoint config. At least one of them is required',\n      ],\n      [\n        {\n          authorization: \"http://a\",\n          token: { url: \"http://a\" },\n          type: \"oauth\",\n          id: \"provider-id\",\n          name: \"\",\n        },\n        'Provider \"provider-id\" is missing both `issuer` and `userinfo` endpoint config. At least one of them is required',\n      ],\n      [\n        {\n          authorization: \"http://a\",\n          token: \"http://a\",\n          // @ts-expect-error Purposefully testing invalid config\n          userinfo: { foo: \"http://a\" },\n          type: \"oauth\",\n          id: \"oauth-provider\",\n          name: \"\",\n        },\n        'Provider \"oauth-provider\" is missing both `issuer` and `userinfo` endpoint config. At least one of them is required',\n      ],\n    ])(\"OAuth/OIDC: invalid endpoints %j\", async (provider, error) => {\n      const { response, logger } = await makeAuthRequest({\n        action: \"providers\",\n        config: { providers: [provider] },\n      })\n\n      expect(response.status).toBe(500)\n      expect(await response.json()).toEqual({\n        message:\n          \"There was a problem with the server configuration. Check the server logs for more information.\",\n      })\n      expect(logger?.error).toHaveBeenCalledWith(new InvalidEndpoints(error))\n    })\n\n    it.each<[string, Provider, Error, Partial<AuthConfig>]>([\n      [\n        \"missing authorize() (function)\",\n        () => ({ type: \"credentials\", id: \"provider-id\", name: \"\" }),\n        new MissingAuthorize(\n          \"Must define an authorize() handler to use credentials authentication provider\"\n        ),\n      ],\n      [\n        \"missing authorize() (object)\",\n        { type: \"credentials\", id: \"provider-id\", name: \"\" },\n        new MissingAuthorize(\n          \"Must define an authorize() handler to use credentials authentication provider\"\n        ),\n      ],\n      [\n        \"trying to use database strategy\",\n        Credentials({}),\n        new UnsupportedStrategy(\n          \"Signing in with credentials only supported if JWT strategy is enabled\"\n        ),\n        { session: { strategy: \"database\" } },\n      ],\n    ] as any)(\"credentials: %s\", async (_, provider, error, config) => {\n      const { response, logger } = await makeAuthRequest({\n        action: \"providers\",\n        config: { ...config, providers: [provider] },\n      })\n\n      expect(response.status).toBe(500)\n      expect(await response.json()).toEqual({\n        message:\n          \"There was a problem with the server configuration. Check the server logs for more information.\",\n      })\n      expect(logger?.error).toHaveBeenCalledWith(error)\n    })\n  })\n})\n"
  },
  {
    "path": "packages/core/test/authorize.test.ts",
    "content": "import { describe, expect, it, vi, beforeEach } from \"vitest\"\nimport { Auth, AuthConfig, skipCSRFCheck } from \"../src\"\nimport { Adapter } from \"../src/adapters\"\nimport SendGrid from \"../src/providers/sendgrid\"\nimport { getUserAndAccount } from \"../src/lib/actions/callback/oauth/callback\"\nimport { getUserAndAccountArgs } from \"./fixtures/oauth-callback.ts\"\n\nconst mockAdapter: Adapter = {\n  createVerificationToken: vi.fn(),\n  useVerificationToken: vi.fn(),\n  getUserByEmail: vi.fn(),\n}\nconst logger = { error: vi.fn() }\n\nasync function signIn(config: Partial<AuthConfig> = {}) {\n  return (await Auth(\n    new Request(\"http://a/auth/signin/sendgrid\", {\n      method: \"POST\",\n      body: new URLSearchParams({ email: \"a@b.c\" }),\n    }),\n    {\n      secret: \"secret\",\n      trustHost: true,\n      logger,\n      adapter: mockAdapter,\n      skipCSRFCheck,\n      providers: [SendGrid],\n      ...config,\n    }\n  )) as Response\n}\n\ndescribe(\"auth via callbacks.signIn\", () => {\n  beforeEach(() => {\n    logger.error.mockReset()\n  })\n  describe(\"redirect before sending an email\", () => {\n    it(\"return false\", async () => {\n      const res = await signIn({ callbacks: { signIn: () => false } })\n      expect(res.headers.get(\"Location\")).toBe(\n        \"http://a/auth/error?error=AccessDenied\"\n      )\n    })\n    it(\"return redirect relative URL\", async () => {\n      const res = await signIn({ callbacks: { signIn: () => \"/wrong\" } })\n      expect(res.headers.get(\"Location\")).toBe(\"http://a/wrong\")\n    })\n\n    it(\"return redirect absolute URL, different domain\", async () => {\n      const res = await signIn({ callbacks: { signIn: () => \"/wrong\" } })\n      const redirect = res.headers.get(\"Location\")\n      // Not allowed by our default redirect callback\n      expect(redirect).not.toBe(\"http://b/wrong\")\n      expect(redirect).toBe(\"http://a/wrong\")\n    })\n\n    it(\"throw error\", async () => {\n      const e = new Error(\"my error\")\n      const res = await signIn({\n        callbacks: {\n          signIn() {\n            throw e\n          },\n        },\n      })\n      expect(res.headers.get(\"Location\")).toBe(\n        \"http://a/auth/error?error=AccessDenied\"\n      )\n    })\n  })\n\n  describe(\"oauth callback\", () => {\n    it(\"should build the correct user object\", async () => {\n      const { profile, provider, tokens, logger } = getUserAndAccountArgs\n      const profileResult = await getUserAndAccount(\n        profile,\n        provider,\n        tokens,\n        logger\n      )\n\n      expect(profileResult?.account.type).toBe(\"oauth\")\n      expect(profileResult?.account.provider).toBe(\"github\")\n      expect(profileResult?.account.providerAccountId).toBe(\"abc\")\n      expect(profileResult?.user.email).toBe(\"fill@murray.com\")\n\n      // Test 'user.id' is a valid UUIDv4 from `crypto.randomUUID()`\n      expect(\n        profileResult?.user.id.match(\n          /^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i\n        )?.[0]\n      ).toBe(profileResult?.user.id)\n    })\n  })\n\n  // TODO: We need an OAuth provider to test against\n  describe.todo(\"redirect in oauth\", () => {})\n})\n"
  },
  {
    "path": "packages/core/test/env.test.ts",
    "content": "import { beforeEach, describe, expect, it, vi } from \"vitest\"\n\nimport { AuthConfig } from \"../src/index.js\"\nimport { setEnvDefaults, createActionURL } from \"../src/lib/utils/env.js\"\nimport Auth0 from \"../src/providers/auth0.js\"\nimport Resend from \"../src/providers/resend.js\"\n\nconst logger = { warn: vi.fn() }\n\ndescribe(\"config is inferred from environment variables\", () => {\n  const testConfig: AuthConfig = {\n    providers: [Auth0, Resend({})],\n    logger,\n  }\n\n  let authConfig: AuthConfig\n\n  beforeEach(() => {\n    authConfig = { ...testConfig } // clone\n    vi.resetAllMocks()\n  })\n\n  it(\"providers (client id, client secret, issuer, api key)\", () => {\n    const env = {\n      AUTH_AUTH0_ID: \"asdf\",\n      AUTH_AUTH0_SECRET: \"fdsa\",\n      AUTH_AUTH0_ISSUER: \"https://example.com\",\n      AUTH_RESEND_KEY: \"resend\",\n    }\n    setEnvDefaults(env, authConfig)\n    const [p1, p2] = authConfig.providers\n    // @ts-expect-error\n    expect(p1.clientId).toBe(env.AUTH_AUTH0_ID)\n    // @ts-expect-error\n    expect(p1.clientSecret).toBe(env.AUTH_AUTH0_SECRET)\n    // @ts-expect-error\n    expect(p1.issuer).toBe(env.AUTH_AUTH0_ISSUER)\n    // @ts-expect-error\n    expect(p2.apiKey).toBe(env.AUTH_RESEND_KEY)\n    expect(logger.warn).not.toHaveBeenCalled()\n  })\n\n  it(\"AUTH_SECRET\", () => {\n    const env = { AUTH_SECRET: \"secret\" }\n    setEnvDefaults(env, authConfig)\n    expect(authConfig.secret?.[0]).toBe(env.AUTH_SECRET)\n    expect(logger.warn).not.toHaveBeenCalled()\n  })\n\n  it(\"AUTH_SECRET, prefer config\", () => {\n    const env = { AUTH_SECRET: \"0\", AUTH_SECRET_1: \"1\" }\n    setEnvDefaults(env, authConfig)\n    expect(authConfig.secret).toEqual([\"1\", \"0\"])\n    expect(logger.warn).not.toHaveBeenCalled()\n  })\n\n  it(\"AUTH_SECRET, prefer config\", () => {\n    const env = { AUTH_SECRET: \"new\" }\n    authConfig.secret = [\"old\"]\n    setEnvDefaults(env, authConfig)\n    expect(authConfig.secret).toEqual([\"old\"])\n    expect(logger.warn).not.toHaveBeenCalled()\n  })\n\n  it(\"AUTH_REDIRECT_PROXY_URL\", () => {\n    const env = { AUTH_REDIRECT_PROXY_URL: \"http://example.com\" }\n    setEnvDefaults(env, authConfig)\n    expect(authConfig.redirectProxyUrl).toBe(env.AUTH_REDIRECT_PROXY_URL)\n    expect(logger.warn).not.toHaveBeenCalled()\n  })\n\n  it(\"AUTH_URL\", () => {\n    const env = { AUTH_URL: \"http://n/api/auth\" }\n    setEnvDefaults(env, authConfig)\n    expect(authConfig.basePath).toBe(\"/api/auth\")\n    expect(logger.warn).not.toHaveBeenCalled()\n  })\n\n  it(\"AUTH_URL + prefer config\", () => {\n    const env = { AUTH_URL: \"http://n/api/auth\" }\n    const fromConfig = \"/basepath-from-config\"\n    authConfig.basePath = fromConfig\n    setEnvDefaults(env, authConfig)\n    expect(authConfig.basePath).toBe(fromConfig)\n    expect(logger.warn).toHaveBeenCalledWith(\"env-url-basepath-redundant\")\n  })\n\n  it(\"AUTH_URL + prefer config but suppress base path waring\", () => {\n    const env = { AUTH_URL: \"http://n/api/auth\" }\n    const fromConfig = \"/basepath-from-config\"\n    authConfig.basePath = fromConfig\n    setEnvDefaults(env, authConfig, true)\n    expect(authConfig.basePath).toBe(fromConfig)\n    expect(logger.warn).not.toHaveBeenCalled()\n  })\n\n  it(\"AUTH_URL, but invalid value\", () => {\n    const env = { AUTH_URL: \"secret\" }\n    setEnvDefaults(env, authConfig)\n    expect(authConfig.basePath).toBe(\"/auth\")\n    expect(logger.warn).not.toHaveBeenCalled()\n  })\n\n  it.each([\n    [{ AUTH_TRUST_HOST: \"1\" }, { trustHost: true }],\n    [{ VERCEL: \"1\" }, { trustHost: true }],\n    [{ NODE_ENV: \"development\" }, { trustHost: true }],\n    [{ NODE_ENV: \"test\" }, { trustHost: true }],\n    [{ AUTH_URL: \"http://example.com\" }, { trustHost: true }],\n  ])(`%j`, (env, expected) => {\n    setEnvDefaults(env, authConfig)\n    expect(authConfig).toMatchObject(expected)\n    expect(logger.warn).not.toHaveBeenCalled()\n  })\n})\n\ndescribe(\"createActionURL\", () => {\n  beforeEach(() => {\n    vi.resetAllMocks()\n  })\n\n  it.each([\n    {\n      args: {\n        action: \"callback\",\n        protocol: undefined,\n        headers: new Headers({ host: \"example.com\" }),\n        env: {},\n        config: { basePath: \"/basepath\" },\n      },\n      expected: \"https://example.com/basepath/callback\",\n    },\n    {\n      args: {\n        action: \"session\",\n        protocol: \"http\",\n        headers: new Headers({ host: \"example.com\" }),\n        env: {},\n        config: { basePath: \"/auth\" },\n      },\n      expected: \"http://example.com/auth/session\",\n    },\n    {\n      args: {\n        action: \"session\",\n        protocol: \"http\",\n        headers: new Headers({\n          host: \"127.0.0.1\",\n          \"x-forwarded-host\": \"example.com\",\n        }),\n        env: {},\n        config: { basePath: \"/auth\" },\n      },\n      expected: \"http://example.com/auth/session\",\n    },\n    {\n      args: {\n        action: \"signin\",\n        protocol: \"http\",\n        headers: new Headers({\n          \"x-forwarded-host\": \"example.com\",\n          \"x-forwarded-proto\": \"https\",\n        }),\n        env: {},\n        config: { basePath: \"/auth\" },\n      },\n      expected: \"https://example.com/auth/signin\",\n    },\n    {\n      args: {\n        action: \"signin\",\n        protocol: \"http:\",\n        headers: new Headers({\n          \"x-forwarded-host\": \"example.com\",\n        }),\n        env: {},\n        config: { basePath: \"/auth\" },\n      },\n      expected: \"http://example.com/auth/signin\",\n    },\n    {\n      args: {\n        action: \"signin\",\n        protocol: \"https:\",\n        headers: new Headers({\n          \"x-forwarded-host\": \"example.com\",\n        }),\n        env: {},\n        config: { basePath: \"/auth\" },\n      },\n      expected: \"https://example.com/auth/signin\",\n    },\n    {\n      args: {\n        action: \"signin\",\n        protocol: undefined,\n        headers: new Headers({\n          \"x-forwarded-host\": \"example.com\",\n          \"x-forwarded-proto\": \"https\",\n        }),\n        env: {},\n        config: { basePath: \"/auth\" },\n      },\n      expected: \"https://example.com/auth/signin\",\n    },\n    {\n      args: {\n        action: \"signin\",\n        protocol: undefined,\n        headers: new Headers({\n          \"x-forwarded-host\": \"example.com\",\n          \"x-forwarded-proto\": \"http\",\n        }),\n        env: {},\n        config: { basePath: \"/auth\" },\n      },\n      expected: \"http://example.com/auth/signin\",\n    },\n    {\n      args: {\n        action: \"signout\",\n        protocol: undefined,\n        headers: new Headers({}),\n        env: { AUTH_URL: \"http://localhost:3000\" },\n        config: { basePath: \"/api/auth\" },\n      },\n      expected: \"http://localhost:3000/api/auth/signout\",\n    },\n    {\n      args: {\n        action: \"signout\",\n        protocol: undefined,\n        headers: new Headers({}),\n        env: { AUTH_URL: \"https://sub.domain.env.com\" },\n        config: { basePath: \"/api/auth\" },\n      },\n      expected: \"https://sub.domain.env.com/api/auth/signout\",\n    },\n    {\n      args: {\n        action: \"signout\",\n        protocol: undefined,\n        headers: new Headers({}),\n        env: { AUTH_URL: \"https://sub.domain.env.com/api/auth\" },\n        config: { basePath: undefined },\n      },\n      expected: \"https://sub.domain.env.com/api/auth/signout\",\n    },\n    {\n      args: {\n        action: \"signout\",\n        protocol: undefined,\n        headers: new Headers({}),\n        env: { AUTH_URL: \"http://localhost:3000/my-app/api/auth\" },\n        config: { basePath: \"/my-app/api/auth\" },\n      },\n      expected: \"http://localhost:3000/my-app/api/auth/signout\",\n    },\n  ])(\"%j\", ({ args, expected }) => {\n    const argsWithLogger = { ...args, config: { ...args.config, logger } }\n    // @ts-expect-error\n    expect(createActionURL(...Object.values(argsWithLogger)).toString()).toBe(\n      expected\n    )\n    expect(logger.warn).not.toHaveBeenCalled()\n  })\n\n  it.each([\n    {\n      args: {\n        action: \"signout\",\n        protocol: undefined,\n        headers: new Headers({}),\n        env: { AUTH_URL: \"http://localhost:3000/my-app/api/auth/\" },\n        config: { basePath: \"/my-app/api/auth\" },\n      },\n      expected: {\n        url: \"http://localhost:3000/my-app/api/auth/signout\",\n        warningMessage: \"env-url-basepath-mismatch\",\n      },\n    },\n    {\n      args: {\n        action: \"signout\",\n        protocol: undefined,\n        headers: new Headers({}),\n        env: { AUTH_URL: \"https://sub.domain.env.com/my-app\" },\n        config: { basePath: \"/api/auth\" },\n      },\n      expected: {\n        url: \"https://sub.domain.env.com/api/auth/signout\",\n        warningMessage: \"env-url-basepath-mismatch\",\n      },\n    },\n  ])(\"Duplicate path configurations: %j\", ({ args, expected }) => {\n    const argsWithLogger = { ...args, config: { ...args.config, logger } }\n    // @ts-expect-error\n    expect(createActionURL(...Object.values(argsWithLogger)).toString()).toBe(\n      expected.url\n    )\n    expect(logger.warn).toHaveBeenCalledWith(expected.warningMessage)\n  })\n})\n"
  },
  {
    "path": "packages/core/test/fixtures/oauth-callback.ts",
    "content": "const userProfile = {\n  id: \"abc\",\n  name: \"Fill Murray\",\n  email: \"fill@murray.com\",\n  image: \"https://source.boringavatars.com/marble\",\n}\n\nexport const getUserAndAccountArgs = {\n  profile: {\n    login: \"fmurray\",\n    id: 7415983,\n    node_id: \"aDn61Xa3cjk0MT05gDQ=\",\n    gravatar_id: \"\",\n    type: \"User\",\n    site_admin: false,\n    name: \"Fill Murray\",\n    company: null,\n    email: \"fill@murray.com\",\n    hireable: null,\n    bio: \"CEO of HTMX\",\n    twitter_username: \"\",\n    public_repos: 146,\n    public_gists: 39,\n    followers: 314,\n    following: 86,\n    created_at: \"2014-04-26T19:35:49Z\",\n    updated_at: \"2024-01-27T20:46:58Z\",\n    private_gists: 19,\n    total_private_repos: 20,\n    owned_private_repos: 20,\n    disk_usage: 768655,\n    collaborators: 6,\n    two_factor_authentication: true,\n    plan: {\n      name: \"free\",\n      space: 976562499,\n      collaborators: 0,\n      private_repos: 10000,\n    },\n  },\n  provider: {\n    id: \"github\",\n    name: \"GitHub\",\n    type: \"oauth\" as const,\n    authorization: {\n      url: new URL(\"https://google.com\"),\n      request: undefined,\n      conform: undefined,\n    },\n    token: { url: new URL(\"https://google.com\") },\n    userinfo: {\n      url: new URL(\"https://google.com\"),\n      // request: async () => {},\n      conform: undefined,\n    },\n    profile: () => userProfile,\n    style: { logo: \"/github.svg\", bg: \"#24292f\", text: \"#fff\" },\n    clientId: \"abc\",\n    clientSecret: \"abc\",\n    signinUrl: \"http://localhost:3000/api/auth/signin/github\",\n    callbackUrl: \"http://localhost:3000/api/auth/callback/github\",\n    redirectProxyUrl: undefined,\n    checks: [\"pkce\" as const],\n    account: () => {},\n  },\n  tokens: {\n    access_token: \"gho_abc\",\n    token_type: \"bearer\" as const,\n    scope: \"read:user,user:email\",\n  },\n  logger: {\n    error: (error: Error) => console.error(error),\n    warn: (error: string) => console.warn(error),\n    debug: (error: string) => console.debug(error),\n  },\n}\n"
  },
  {
    "path": "packages/core/test/fixtures/pages.ts",
    "content": "import type { AuthConfig } from \"../../src/types\"\n\nconst userProfile = {\n  id: \"abc\",\n  name: \"Fill Murray\",\n  email: \"fill@murray.com\",\n  image: \"https://source.boringavatars.com/marble\",\n}\n\nexport const authOptions = {\n  debug: false,\n  pages: {},\n  theme: {\n    colorScheme: \"auto\" as const,\n    logo: \"\",\n    brandColor: \"\",\n    buttonText: \"\",\n  },\n  session: {\n    strategy: \"jwt\" as const,\n    maxAge: 2592000,\n    updateAge: 86400,\n    generateSessionToken: () => \"abc123\",\n  },\n  providers: [\n    {\n      id: \"github\",\n      name: \"GitHub\",\n      type: \"oauth\" as const,\n      authorization: `https://github.com/login/oauth/authorize`,\n      token: `https://github.com/login/oauth/access_token`,\n      userinfo: {\n        url: `https://github.com/user`,\n        async request() {\n          return userProfile\n        },\n      },\n      profile: () => userProfile,\n      style: { logo: \"\", bg: \"\", text: \"\" },\n      clientId: \"abc\",\n      clientSecret: \"abc\",\n      redirectProxyUrl: undefined,\n      checks: [],\n      account: () => {},\n    },\n  ],\n  basePath: \"/auth\",\n  secret: [\"abc\"],\n  redirectProxyUrl: undefined,\n  trustHost: true,\n  adapter: undefined,\n  logger: {\n    error: (msg: Error) => console.error(msg),\n  },\n  experimental: {},\n} satisfies AuthConfig\n"
  },
  {
    "path": "packages/core/test/jwt.test.ts",
    "content": "import { describe, it, expect } from \"vitest\"\nimport { encode, decode } from \"../jwt\"\n\ndescribe(\"supports secret rotation\", () => {\n  const token = { foo: \"bar\" }\n  const s1 = \"secret1\"\n  const s2 = \"secret2\"\n  const s3 = \"secret3\"\n  const s12 = [s1, s2]\n\n  const salt = \"salt\"\n\n  it(\"can decode tokens that were encoded with different secrets\", async () => {\n    const t1 = await encode({ salt, token, secret: s1 })\n    const d1 = await decode({ salt, token: t1, secret: s12 })\n    expect(d1?.foo).toEqual(token.foo)\n\n    const t2 = await encode({ salt, token, secret: s2 })\n    const d2 = await decode({ salt, token: t2, secret: s12 })\n    expect(d2?.foo).toEqual(token.foo)\n  })\n\n  it(\"always encodes with leftmost secret\", async () => {\n    const t1 = await encode({ salt, token, secret: s12 })\n\n    const d1 = await decode({ salt, token: t1, secret: s1 })\n    expect(d1?.foo).toEqual(token.foo)\n\n    const d2 = decode({ salt, token: t1, secret: s2 })\n    expect(() => d2).rejects.toEqual(new Error(\"no matching decryption secret\"))\n  })\n\n  it(\"should not be able decode with non-matching secret\", async () => {\n    const t1 = await encode({ salt, token, secret: s3 })\n    const decoded = decode({ salt, token: t1, secret: s12 })\n    expect(() => decoded).rejects.toEqual(\n      new Error(\"no matching decryption secret\")\n    )\n  })\n})\n"
  },
  {
    "path": "packages/core/test/memory-adapter.ts",
    "content": "/**\n * This is a simple in-memory adapter for NextAuth.js that is intended for use in tests only.\n * Original source: https://github.com/nextauthjs/next-auth/pull/8809\n */\nimport {\n  Adapter,\n  AdapterAccount,\n  AdapterSession,\n  AdapterUser,\n  VerificationToken,\n} from \"../adapters.js\"\n\n/**\n * Represents the in-memory data structure for the adapter.\n */\nexport type Memory = {\n  users: Map<string, AdapterUser>\n  accounts: Map<string, AdapterAccount>\n  sessions: Map<string, AdapterSession>\n  verificationTokens: Map<string, VerificationToken>\n}\n\n/**\n * Initializes a new instance of the Memory object.\n * @returns A Memory object with empty maps for users, accounts, sessions, etc.\n */\nexport function initMemory(): Memory {\n  return {\n    users: new Map<string, AdapterUser>(),\n    accounts: new Map<string, AdapterAccount>(),\n    sessions: new Map<string, AdapterSession>(),\n    verificationTokens: new Map<string, VerificationToken>(),\n  }\n}\n\nexport function MemoryAdapter(memory?: Memory): Adapter {\n  const { users, accounts, sessions, verificationTokens } =\n    memory ?? initMemory()\n\n  // Create the adapter object first and then populate it.\n  // This allows us to call adapter functions from within.\n  const adapter: Adapter = {}\n\n  // Assign all functions in place\n  Object.assign(adapter, {\n    async createUser(user: AdapterUser) {\n      const newUser = { ...user, id: makeid(32) }\n      users.set(newUser.id, newUser)\n\n      return newUser\n    },\n    async getUser(id: string) {\n      return users.get(id) ?? null\n    },\n    async getUserByEmail(email: string) {\n      return (\n        Array.from(users.values()).find((user) => user.email === email) ?? null\n      )\n    },\n    async getUserByAccount(\n      providerAccountId: Pick<AdapterAccount, \"provider\" | \"providerAccountId\">\n    ) {\n      const account = accounts.get(providerAccountId.providerAccountId)\n      if (!account) return null\n\n      return users.get(account.userId) ?? null\n    },\n    async updateUser(user: Partial<AdapterUser> & Pick<AdapterUser, \"id\">) {\n      const currentUser = users.get(user.id)\n      if (!currentUser) throw new Error(\"User not found\")\n\n      const updatedUser = { ...currentUser, ...user }\n      users.set(user.id, updatedUser)\n\n      return updatedUser\n    },\n    async deleteUser(id: string) {\n      const user = users.get(id)\n      if (!user) return\n\n      // Delete sessions\n      if (!adapter.deleteSession)\n        throw new Error(\"Adapter does not implement deleteSession!\")\n      const { deleteSession } = adapter\n      sessions.forEach(async (session) => {\n        if (session.userId === user.id) {\n          await deleteSession(session.sessionToken)\n        }\n      })\n\n      // Delete accounts\n      if (!adapter.unlinkAccount)\n        throw new Error(\"Adapter does not implement unlinkAccount!\")\n      const { unlinkAccount } = adapter\n      accounts.forEach(async (account) => {\n        if (account.userId === user.id) {\n          await unlinkAccount(account)\n        }\n      })\n\n      // Delete verification tokens\n      if (!adapter.useVerificationToken)\n        throw new Error(\"Adapter does not implement useVerificationToken!\")\n      const { useVerificationToken } = adapter\n      verificationTokens.forEach(async (verificationToken) => {\n        if (verificationToken.identifier === user.email) {\n          await useVerificationToken(verificationToken)\n        }\n      })\n\n      // Delete user\n      users.delete(id)\n\n      return\n    },\n    async linkAccount(account: AdapterAccount) {\n      accounts.set(account.providerAccountId, account)\n\n      return account\n    },\n    async unlinkAccount(\n      account: Pick<AdapterAccount, \"provider\" | \"providerAccountId\">\n    ) {\n      // Find account\n      const currentAccount = accounts.get(account.providerAccountId)\n      if (!currentAccount) return\n\n      // Delete account\n      accounts.delete(currentAccount.providerAccountId)\n\n      return\n    },\n    async listLinkedAccounts(userId: string) {\n      return Array.from(accounts.values()).filter(\n        (account) => account.userId === userId\n      )\n    },\n    async createSession(session: {\n      sessionToken: string\n      userId: string\n      expires: Date\n    }) {\n      sessions.set(session.sessionToken, session)\n\n      return session\n    },\n    async getSessionAndUser(sessionToken: string) {\n      const session = sessions.get(sessionToken)\n      if (!session) return null\n\n      // Remove if expired\n      if (session.expires < new Date()) {\n        if (!adapter.deleteSession)\n          throw new Error(\"Adapter does not implement deleteSession!\")\n        await adapter.deleteSession(sessionToken)\n\n        return null\n      }\n\n      const user = users.get(session.userId)\n      if (!user) return null\n\n      return { session, user }\n    },\n    async updateSession(\n      session: Partial<AdapterSession> & Pick<AdapterSession, \"sessionToken\">\n    ) {\n      const currentSession = sessions.get(session.sessionToken)\n      if (!currentSession) throw new Error(\"Session not found\")\n\n      const updatedSession = { ...currentSession, ...session }\n      sessions.set(session.sessionToken, updatedSession)\n\n      return updatedSession\n    },\n    async deleteSession(sessionToken: string) {\n      sessions.delete(sessionToken)\n\n      return\n    },\n    async createVerificationToken(verificationToken: VerificationToken) {\n      verificationTokens.set(verificationToken.token, verificationToken)\n\n      return verificationToken\n    },\n    async useVerificationToken(params: { identifier: string; token: string }) {\n      const { token } = params\n\n      // Find verification token\n      const verificationToken = verificationTokens.get(token)\n      if (!verificationToken) return null\n\n      // Delete used verification token\n      verificationTokens.delete(token)\n\n      return verificationToken\n    },\n  } as Adapter)\n\n  return adapter\n}\n\n/**\n * Generates a random string of the specified length.\n * @param length The length of the generated string.\n * @returns The randomly generated string.\n */\nfunction makeid(length: number) {\n  let result = \"\"\n  const alphabet =\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\"\n\n  // Build a string of the specified length by randomly selecting\n  // characters from the alphabet at each iteration.\n  for (let i = 0; i < length; i++) {\n    const randomIndex = Math.floor(Math.random() * alphabet.length)\n    result += alphabet.charAt(randomIndex)\n  }\n\n  return result\n}\n\nexport function asBase64(buffer: Uint8Array): string {\n  return Buffer.from(buffer).toString(\"base64\")\n}\n"
  },
  {
    "path": "packages/core/test/merge.test.ts",
    "content": "import { describe, it, expect } from \"vitest\"\nimport { merge } from \"../src/lib/utils/merge\"\n\ndescribe(\"merge function\", () => {\n  it(\"should merge objects correctly\", () => {\n    expect(merge({ a: 1, b: { c: 2 } }, { b: { c: 3, d: 4 } })).toEqual({\n      a: 1,\n      b: { c: 3, d: 4 },\n    })\n  })\n\n  it(\"should override primitive values\", () => {\n    expect(merge({ a: 1, b: \"old\" }, { b: \"new\" })).toEqual({ a: 1, b: \"new\" })\n  })\n\n  it(\"should override arrays\", () => {\n    expect(merge({ a: [1, 2] }, { a: [3, 4] })).toEqual({ a: [3, 4] })\n  })\n\n  it(\"should handle nested objects\", () => {\n    expect(merge({ a: { b: { c: 1 } } }, { a: { b: { d: 2 } } })).toEqual({\n      a: { b: { c: 1, d: 2 } },\n    })\n  })\n\n  it(\"should handle null and undefined\", () => {\n    expect(\n      merge({ a: undefined, b: undefined }, { a: null, b: 2, c: 3 })\n    ).toEqual({ a: null, b: 2, c: 3 })\n  })\n\n  it(\"should merge multiple objects\", () => {\n    expect(merge({ a: 1 }, { b: 2 }, { b: { d: 2 }, c: \"new\" })).toEqual({\n      a: 1,\n      b: { d: 2 },\n      c: \"new\",\n    })\n  })\n\n  it(\"should handle empty objects\", () => {\n    expect(merge({ a: 1 }, {})).toEqual({ a: 1 })\n  })\n\n  it(\"should override functions\", () => {\n    expect(\n      merge(\n        {\n          func: () => \"original\",\n          nested: { func: () => \"original nested\" },\n        },\n        {\n          func: () => \"overridden\",\n          nested: { func: () => \"overridden nested\" },\n        }\n      ).func()\n    ).toBe(\"overridden\")\n    expect(\n      merge(\n        {\n          func: () => \"original\",\n          nested: { func: () => \"original nested\" },\n        },\n        {\n          func: () => \"overridden\",\n          nested: { func: () => \"overridden nested\" },\n        }\n      ).nested.func()\n    ).toBe(\"overridden nested\")\n  })\n\n  it(\"should override default authorization config with user provided object\", () => {\n    expect(\n      merge(\n        {\n          authorization: \"https://example.com/default\",\n        },\n        {\n          authorization: {\n            url: \"https://example.com/user-config\",\n            params: { scope: \"email,user_friends\" },\n          },\n        }\n      )\n    ).toEqual({\n      authorization: {\n        url: \"https://example.com/user-config\",\n        params: { scope: \"email,user_friends\" },\n      },\n    })\n  })\n\n  it(\"should correctly merge scopes in authorization params\", () => {\n    expect(\n      merge(\n        {\n          authorization: {\n            url: \"https://example.com/default\",\n            params: { scope: \"identify,email\" },\n          },\n          defaultOptions: \"\",\n        },\n        {\n          authorization: \"https://example.com/user-config\",\n        }\n      )\n    ).toEqual({\n      defaultOptions: \"\",\n      authorization: \"https://example.com/user-config\",\n    })\n  })\n})\n"
  },
  {
    "path": "packages/core/test/pages.test.ts",
    "content": "import { vi, expect, it, describe, beforeEach } from \"vitest\"\nimport renderPage from \"../src/lib/pages/index\"\nimport { authOptions } from \"./fixtures/pages\"\nimport { init } from \"../src/lib/init\"\n\ndescribe(\"pages\", () => {\n  describe(\"sign-out\", () => {\n    beforeEach(() => {\n      vi.resetAllMocks()\n    })\n\n    it(\"should attempt to render signout page\", async () => {\n      // Generated when visiting `/auth/signout`\n      const { options } = await init({\n        authOptions,\n        action: \"signout\",\n        providerId: \"github\",\n        url: new URL(\"http://localhost:3000/auth/signout\"),\n        cookies: {},\n        isPost: true,\n        csrfDisabled: true,\n      })\n\n      const render = renderPage({ ...options, query: {}, cookies: [] })\n      const signOutPage = render.signout()\n\n      expect(signOutPage.body).toContain(`<title>Sign Out</title>`)\n      expect(signOutPage.body).toContain(\n        `action=\"http://localhost:3000/auth/signout\"`\n      )\n    })\n\n    it(\"should return correct URL for signout page form action\", async () => {\n      // When visiting `/auth/signout`, for example\n      const { options } = await init({\n        authOptions: authOptions,\n        action: \"signout\",\n        providerId: \"github\",\n        url: new URL(\"http://localhost:3000/auth/signout\"),\n        cookies: {},\n        isPost: true,\n        csrfDisabled: true,\n      })\n\n      expect(options.url).toBeInstanceOf(URL)\n      expect(options.url.toString()).toBe(\"http://localhost:3000/auth/signout\")\n    })\n  })\n  describe(\"sign-in\", () => {\n    beforeEach(() => {\n      vi.resetAllMocks()\n    })\n\n    it(\"should attempt to render signin page\", async () => {\n      // Generated when visiting `/auth/signin`\n      const { options } = await init({\n        authOptions: authOptions,\n        action: \"signin\",\n        providerId: \"github\",\n        url: new URL(\"http://localhost:3000/auth/signin\"),\n        cookies: {},\n        isPost: true,\n        csrfDisabled: true,\n      })\n\n      const render = renderPage({ ...options, query: {}, cookies: [] })\n      const signInPage = render.signin()\n\n      expect(signInPage.body).toContain(`<title>Sign In</title>`)\n      expect(signInPage.body).toContain(\n        `action=\"http://localhost:3000/auth/signin/github\"`\n      )\n    })\n  })\n})\n"
  },
  {
    "path": "packages/core/test/providers/gitlab.test.ts",
    "content": "import { expect, it } from \"vitest\"\nimport GitLab from \"../../src/providers/gitlab\"\n\nit(\"GitLab should handle baseURL correctly\", () => {\n  const config = GitLab({\n    baseUrl: \"https://gitlab.example.com\",\n  })\n  expect(config.id).toBe(\"gitlab\")\n  expect(config.name).toBe(\"GitLab\")\n  expect(config.type).toBe(\"oauth\")\n  expect(config.authorization).toEqual(\n    \"https://gitlab.example.com/oauth/authorize?scope=read_user\"\n  )\n  expect(config.token).toEqual(\"https://gitlab.example.com/oauth/token\")\n  expect(config.userinfo).toEqual(\"https://gitlab.example.com/api/v4/user\")\n})\n"
  },
  {
    "path": "packages/core/test/url-parsing.test.ts",
    "content": "import { describe, expect, it } from \"vitest\"\n\nimport { parseActionAndProviderId } from \"../src/lib/utils/web\"\nimport { UnknownAction } from \"../src/errors\"\n\ndescribe(\"parse the action and provider id\", () => {\n  it.each([\n    {\n      path: \"/auuth/signin\",\n      error: \"Cannot parse action at /auuth/signin\",\n      basePath: \"/auth\",\n    },\n    {\n      path: \"/api/auth/signin\",\n      error: \"Cannot parse action at /api/auth/signin\",\n      basePath: \"/auth\",\n    },\n    {\n      path: \"/auth/auth/signin/github\",\n      error: \"Cannot parse action at /auth/auth/signin/github\",\n      basePath: \"/auth\",\n    },\n    {\n      path: \"/api/auth/signinn\",\n      error: \"Cannot parse action at /api/auth/signinn\",\n      basePath: \"/api/auth\",\n    },\n    {\n      path: \"/api/auth/signinn/github\",\n      error: \"Cannot parse action at /api/auth/signinn/github\",\n      basePath: \"/api/auth\",\n    },\n    {\n      path: \"/api/auth/signin/github/\",\n      action: \"signin\",\n      providerId: \"github\",\n      basePath: \"/api/auth\",\n    },\n    {\n      path: \"/api/auth/signin/github\",\n      action: \"signin\",\n      providerId: \"github\",\n      basePath: \"/api/auth\",\n    },\n    {\n      path: \"/api/auth/signin/github/github\",\n      error: \"Cannot parse action at /api/auth/signin/github/github\",\n      basePath: \"/api/auth\",\n    },\n    {\n      path: \"/api/auth/signin/api/auth/signin/github\",\n      error: \"Cannot parse action at /api/auth/signin/api/auth/signin/github\",\n      basePath: \"/api/auth\",\n    },\n    {\n      path: \"/auth/signin/auth0/\",\n      action: \"signin\",\n      providerId: \"auth0\",\n      basePath: \"/auth\",\n    },\n    {\n      path: \"/auth/signin/auth0///\",\n      action: \"signin\",\n      providerId: \"auth0\",\n      basePath: \"/auth\",\n    },\n    {\n      path: \"/auth/signin/auth0\",\n      action: \"signin\",\n      providerId: \"auth0\",\n      basePath: \"/auth\",\n    },\n    {\n      path: \"/auth/signin/undefined\",\n      action: \"signin\",\n      providerId: undefined,\n      basePath: \"/auth\",\n    },\n  ])(\"$path\", ({ path, error, basePath, action, providerId }) => {\n    if (action || providerId) {\n      const parsed = parseActionAndProviderId(path, basePath)\n      expect(parsed.action).toBe(action)\n      expect(parsed.providerId).toBe(providerId)\n    } else {\n      expect(() => parseActionAndProviderId(path, basePath)).toThrow(\n        new UnknownAction(error)\n      )\n    }\n  })\n})\n"
  },
  {
    "path": "packages/core/test/utils.ts",
    "content": "import { expect, vi } from \"vitest\"\nimport { Auth, createActionURL } from \"../src\"\n\nimport type { Adapter } from \"../src/adapters\"\nimport type { AuthAction, AuthConfig, LoggerInstance } from \"../src/types\"\nimport { defaultCallbacks } from \"../src/lib/init.ts\"\n\nexport const AUTH_SECRET = \"secret\"\nexport const SESSION_COOKIE_NAME = \"__Secure-authjs.session-token\"\nexport const CSRF_COOKIE_NAME = \"__Host-authjs.csrf-token\"\n\nexport function TestAdapter(): Adapter {\n  return {\n    createUser: vi.fn(),\n    getUser: vi.fn(),\n    getUserByEmail: vi.fn(),\n    getUserByAccount: vi.fn(),\n    updateUser: vi.fn(),\n    deleteUser: vi.fn(),\n    linkAccount: vi.fn(),\n    unlinkAccount: vi.fn(),\n    createSession: vi.fn(),\n    getSessionAndUser: vi.fn(),\n    updateSession: vi.fn(),\n    deleteSession: vi.fn(),\n    createVerificationToken: vi.fn(),\n    useVerificationToken: vi.fn(),\n  }\n}\n\nexport const logger: LoggerInstance = {\n  debug: vi.fn(),\n  warn: vi.fn(),\n  error: vi.fn(),\n}\n\nexport const events = {\n  signIn: vi.fn(),\n  signOut: vi.fn(),\n  createUser: vi.fn(),\n  updateUser: vi.fn(),\n  linkAccount: vi.fn(),\n  session: vi.fn(),\n} satisfies AuthConfig[\"events\"]\n\nexport const callbacks = defaultCallbacks\n\nexport const getExpires = (maxAge = 30 * 24 * 60 * 60 * 1000) => {\n  const now = Date.now()\n  vi.setSystemTime(now)\n  return new Date(now + maxAge)\n}\n\nexport function testConfig(overrides?: Partial<AuthConfig>): AuthConfig {\n  return {\n    secret: \"secret\",\n    trustHost: true,\n    logger,\n    events,\n    callbacks,\n    basePath: \"/auth\",\n    providers: [],\n    ...overrides,\n  }\n}\n\nexport async function makeAuthRequest(params: {\n  action: AuthAction\n  cookies?: Record<string, string>\n  host?: string\n  path?: string\n  query?: Record<string, string>\n  body?: any\n  config?: Partial<AuthConfig>\n}) {\n  const { action, body, cookies = {}, host = \"authjs.test\" } = params\n  const config = testConfig(params.config)\n  const headers = new Headers({ host: host })\n  for (const [name, value] of Object.entries(cookies))\n    headers.append(\"cookie\", `${name}=${value}`)\n\n  let url: string | URL = createActionURL(action, \"https\", headers, {}, config)\n  if (params.path) url = `${url}${params.path}`\n  if (params.query) url = `${url}?${new URLSearchParams(params.query)}`\n  const request = new Request(url, {\n    method: body ? \"POST\" : \"GET\",\n    headers,\n    body,\n  })\n  const response = (await Auth(request, config)) as Response\n  return {\n    response,\n    logger: config.logger,\n  }\n}\n\nexport const assertNoCacheResponseHeaders = (response: Response) => {\n  expect(response.headers.get(\"Content-Type\")).toEqual(\"application/json\")\n  expect(response.headers.get(\"Cache-Control\")).toEqual(\n    \"private, no-cache, no-store\"\n  )\n  expect(response.headers.get(\"Expires\")).toEqual(\"0\")\n  expect(response.headers.get(\"Pragma\")).toEqual(\"no-cache\")\n}\n"
  },
  {
    "path": "packages/core/test/webauthn-utils.test.ts",
    "content": "import { describe, expect, it, vi, test, afterEach } from \"vitest\"\nimport {\n  GenerateAuthenticationOptionsOpts,\n  GenerateRegistrationOptionsOpts,\n  generateAuthenticationOptions,\n  generateRegistrationOptions,\n  verifyAuthenticationResponse,\n  verifyRegistrationResponse,\n} from \"@simplewebauthn/server\"\nimport type {\n  Adapter,\n  AdapterAccount,\n  AdapterUser,\n  AdapterAuthenticator,\n} from \"../src/adapters\"\nimport WebAuthn, {\n  GetUserInfo,\n  WebAuthnConfig,\n  WebAuthnProviderType,\n} from \"../src/providers/webauthn\"\nimport {\n  WebAuthnAction,\n  assertInternalOptionsWebAuthn,\n  getAuthenticationResponse,\n  getRegistrationResponse,\n  inferWebAuthnOptions,\n  verifyAuthenticate,\n  verifyRegister,\n  fromBase64,\n  toBase64,\n  stringToTransports,\n  transportsToString,\n} from \"../src/lib/utils/webauthn-utils\"\nimport { webauthnChallenge } from \"../src/lib/actions/callback/oauth/checks\"\nimport {\n  InternalOptions,\n  InternalProvider,\n  RequestInternal,\n} from \"../src/types\"\nimport {\n  AdapterError,\n  AuthError,\n  InvalidProvider,\n  MissingAdapter,\n  WebAuthnVerificationError,\n} from \"../src/errors\"\nimport { randomString } from \"../src/lib/utils/web\"\nimport { PublicKeyCredentialCreationOptionsJSON } from \"@simplewebauthn/server/script/deps\"\nimport { Cookie } from \"../src/lib/utils/cookie\"\nimport { randomInt } from \"crypto\"\n\nconst getMockAdapter = () =>\n  ({\n    getAuthenticator: vi.fn(),\n    updateAuthenticatorCounter: vi.fn(),\n    getAccount: vi.fn(),\n    listAuthenticatorsByUserId: vi.fn(),\n    getUser: vi.fn(),\n  }) as unknown as Required<Adapter>\n\nfunction getMockOptions(\n  defaultOptions?: Partial<InternalOptions>,\n  defaultProvider?: Partial<WebAuthnConfig>\n) {\n  const {\n    adapter = getMockAdapter(),\n    provider = WebAuthn(defaultProvider ?? {}),\n    experimental = { enableWebAuthn: true },\n    action = \"webauthn-options\",\n    logger = { error: vi.fn() },\n    url = new URL(\"http://myapp.com:3000\"),\n    ...rest\n  } = defaultOptions ?? {}\n\n  rest.providers ??= [provider as InternalProvider]\n\n  return {\n    adapter,\n    provider,\n    experimental,\n    url,\n    logger,\n    action,\n    ...rest,\n  } as InternalOptions<WebAuthnProviderType> & { adapter: Required<Adapter> }\n}\n\nfunction createAuthenticator(\n  partial?: Partial<AdapterAuthenticator>\n): AdapterAuthenticator {\n  const id = randomString(32)\n  return {\n    userId: randomString(32),\n    credentialID: id,\n    providerAccountId: id,\n    counter: 0,\n    credentialBackedUp: false,\n    credentialDeviceType: \"platform\",\n    credentialPublicKey: randomString(32),\n    transports: \"usb,ble,nfc\",\n    ...partial,\n  }\n}\n\nfunction getExpectedResponse(\n  action: WebAuthnAction,\n  options: unknown,\n  cookies: Cookie[] = []\n) {\n  const cookie = {\n    name: \"test\",\n    value: \"test\",\n    options: {},\n  }\n  vi.mocked(webauthnChallenge.create).mockResolvedValue({ cookie })\n  return {\n    status: 200,\n    cookies: [...cookies, cookie],\n    body: {\n      action,\n      options,\n    },\n    headers: {\n      \"Content-Type\": \"application/json\",\n    },\n  }\n}\n\n/**\n * Generates default params for verifyAuthenticate and verifyRegister tests\n */\nfunction prepareVerifyTest(\n  action: WebAuthnAction,\n  requestData?: Record<string, unknown>\n) {\n  const options = getMockOptions()\n  const credentialID = toBase64(new Uint8Array([1, 2, 3, 4, 5]))\n  requestData ??= {\n    key: \"value\",\n    id: credentialID,\n    response: { transports: [\"ble\", \"nfc\"] },\n  }\n  const request = {\n    body: { data: JSON.stringify(requestData) },\n    cookies: \"reqcookies\",\n  } as unknown as RequestInternal\n  const cookies = [{ name: \"other\", value: \"value\", options: {} }]\n\n  const authenticator = createAuthenticator({\n    credentialID,\n    providerAccountId: credentialID,\n    // @ts-expect-error\n    transports: requestData.response?.transports\n      ? transportsToString(requestData.response?.transports)\n      : \"usb,ble,nfc\",\n  })\n  vi.mocked(options.adapter.getAuthenticator).mockResolvedValue(authenticator)\n\n  const challenge = \"mychallenge\"\n  vi.mocked(webauthnChallenge.use).mockResolvedValue({ challenge })\n\n  const newCounter = authenticator.counter + randomInt(100)\n  if (action === \"authenticate\") {\n    vi.mocked(verifyAuthenticationResponse).mockResolvedValue({\n      verified: true,\n      // @ts-expect-error\n      authenticationInfo: {\n        newCounter,\n      },\n    })\n  } else {\n    vi.mocked(verifyRegistrationResponse).mockResolvedValue({\n      verified: true,\n      registrationInfo: {\n        counter: authenticator.counter,\n        credentialID: fromBase64(authenticator.credentialID),\n        credentialPublicKey: fromBase64(authenticator.credentialPublicKey),\n        credentialBackedUp: authenticator.credentialBackedUp,\n        // @ts-expect-error\n        credentialDeviceType: authenticator.credentialDeviceType,\n      },\n    })\n  }\n\n  const account = {\n    providerAccountId: authenticator.providerAccountId,\n    userId: authenticator.userId,\n    type: options.provider.type,\n    provider: options.provider.id,\n  } as unknown as AdapterAccount\n  vi.mocked(options.adapter.getAccount).mockResolvedValue(account)\n\n  const user = {\n    id: authenticator.userId,\n    email: \"user@example.com\",\n  } as unknown as AdapterUser\n  vi.mocked(options.adapter.getUser).mockResolvedValue(user)\n\n  const rp = options.provider.getRelayingParty(options, {} as RequestInternal)\n  const expectedAuthenticationResponse = {\n    ...defaultWebAuthnConfig.verifyAuthenticationOptions,\n    expectedChallenge: challenge,\n    response: requestData,\n    authenticator: {\n      ...authenticator,\n      credentialID: fromBase64(authenticator.credentialID),\n      transports: stringToTransports(authenticator.transports),\n      credentialPublicKey: fromBase64(authenticator.credentialPublicKey),\n    },\n    expectedOrigin: rp.origin,\n    expectedRPID: rp.id,\n  }\n\n  const expectedRegistrationResponse = {\n    ...defaultWebAuthnConfig.verifyRegistrationOptions,\n    expectedChallenge: challenge,\n    response: requestData,\n    expectedOrigin: rp.origin,\n    expectedRPID: rp.id,\n  }\n\n  return {\n    user,\n    account,\n    options,\n    request,\n    cookies,\n    newCounter,\n    credentialID,\n    authenticator,\n    expectedRegistrationResponse,\n    expectedAuthenticationResponse,\n  }\n}\n\nvi.mock(\"@simplewebauthn/server\", () => ({\n  generateAuthenticationOptions: vi.fn(),\n  generateRegistrationOptions: vi.fn(),\n  verifyAuthenticationResponse: vi.fn(),\n  verifyRegistrationResponse: vi.fn(),\n}))\n\nvi.mock(\"../src/lib/actions/callback/oauth/checks\", () => ({\n  webauthnChallenge: {\n    create: vi.fn(),\n    use: vi.fn(),\n  },\n}))\n\nconst defaultWebAuthnConfig = WebAuthn({})\n\nafterEach(() => {\n  vi.resetAllMocks()\n})\n\ndescribe(\"assertInternalOptionsWebAuthn\", () => {\n  it(\"accepts valid options\", () => {\n    const options = getMockOptions()\n    expect(assertInternalOptionsWebAuthn(options)).toEqual(options)\n  })\n  it(\"errors on missing adapter\", () => {\n    const options = getMockOptions()\n    options.adapter = undefined as unknown as Required<Adapter>\n    expect(() => assertInternalOptionsWebAuthn(options)).toThrow(MissingAdapter)\n  })\n  it(\"errors on non-webauthn provider\", () => {\n    const options = getMockOptions()\n    options.provider.type = \"email\" as unknown as WebAuthnProviderType\n    expect(() => assertInternalOptionsWebAuthn(options)).toThrow(\n      InvalidProvider\n    )\n  })\n})\n\ndescribe(\"fromBase64\", () => {\n  it(\"decodes base64\", () => {\n    expect(fromBase64(\"AQIDBAU=\")).toEqual(new Uint8Array([1, 2, 3, 4, 5]))\n  })\n\n  it(\"is stable\", () => {\n    const input = new Uint8Array([1, 2, 3, 4, 5])\n    expect(fromBase64(toBase64(input))).toEqual(input)\n  })\n})\n\ndescribe(\"toBase64\", () => {\n  it(\"encodes base64\", () => {\n    expect(toBase64(new Uint8Array([1, 2, 3, 4, 5]))).toEqual(\"AQIDBAU=\")\n  })\n\n  it(\"is stable\", () => {\n    const input = \"AQIDBAU=\"\n    expect(toBase64(fromBase64(input))).toEqual(input)\n  })\n})\n\ndescribe(\"stringToTransports\", () => {\n  it(\"converts string to transports\", () => {\n    expect(stringToTransports(\"usb,ble,nfc\")).toEqual([\"usb\", \"ble\", \"nfc\"])\n  })\n\n  it(\"handles empty string\", () => {\n    expect(stringToTransports(\"\")).toEqual(undefined)\n  })\n\n  it(\"accepts undefined\", () => {\n    expect(stringToTransports(undefined)).toEqual(undefined)\n  })\n})\n\ndescribe(\"transportsToString\", () => {\n  it(\"converts transports to string\", () => {\n    expect(transportsToString([\"usb\", \"ble\", \"nfc\"])).toEqual(\"usb,ble,nfc\")\n  })\n\n  it(\"handles empty array\", () => {\n    expect(transportsToString([])).toEqual(\"\")\n  })\n\n  it(\"accepts undefined\", () => {\n    expect(transportsToString(undefined)).toEqual(undefined)\n  })\n})\n\ndescribe(\"getRelayingParty\", () => {\n  it(\"returns relaying party with default values\", () => {\n    const options = getMockOptions()\n    const relayingParty = options.provider.getRelayingParty(\n      options,\n      {} as RequestInternal\n    )\n\n    expect(relayingParty).toEqual({\n      id: options.url.hostname,\n      name: options.url.host,\n      origin: options.url.origin,\n    })\n  })\n\n  it(\"returns relaying party with custom values\", () => {\n    const options = getMockOptions(\n      {},\n      {\n        relayingParty: {\n          id: \"my-id\",\n          name: \"My Relaying Party\",\n          origin: \"https://custom.com\",\n        },\n      }\n    )\n    const relayingParty = options.provider.getRelayingParty(\n      options,\n      {} as RequestInternal\n    )\n\n    expect(relayingParty).toEqual({\n      id: \"my-id\",\n      name: \"My Relaying Party\",\n      origin: \"https://custom.com\",\n    })\n  })\n\n  it(\"returns relaying party with mixed values\", () => {\n    const options = getMockOptions(\n      {},\n      {\n        relayingParty: {\n          id: \"my-id\",\n          origin: \"https://custom.com\",\n        },\n      }\n    )\n    const relayingParty = options.provider.getRelayingParty(\n      options,\n      {} as RequestInternal\n    )\n\n    expect(relayingParty).toEqual({\n      id: \"my-id\",\n      name: options.url.host,\n      origin: \"https://custom.com\",\n    })\n  })\n\n  it(\"uses the first value if array by default\", () => {\n    const options = getMockOptions(\n      {},\n      {\n        relayingParty: {\n          id: [\"other-id\", \"my-id\"],\n          name: [\"Other Relaying Party\", \"My Relaying Party\"],\n          origin: [\"https://other.com\", \"https://custom.com\"],\n        },\n      }\n    )\n    const relayingParty = options.provider.getRelayingParty(\n      options,\n      {} as RequestInternal\n    )\n\n    expect(relayingParty).toEqual({\n      id: \"other-id\",\n      name: \"Other Relaying Party\",\n      origin: \"https://other.com\",\n    })\n  })\n\n  it(\"accepts custom getRelayingParty function\", () => {\n    const options = getMockOptions(\n      {},\n      {\n        relayingParty: {\n          id: \"my-id\",\n          origin: \"https://custom.com\",\n        },\n        getRelayingParty: (opts, req) => {\n          const id = opts.provider.relayingParty!.id as string\n          return {\n            id,\n            name: req.url.host,\n            origin: req.url.origin,\n          }\n        },\n      }\n    )\n    const relayingParty = options.provider.getRelayingParty(options, {\n      url: new URL(\"https://myapp.com\"),\n    } as RequestInternal)\n\n    expect(relayingParty).toEqual({\n      id: \"my-id\",\n      name: \"myapp.com\",\n      origin: \"https://myapp.com\",\n    })\n  })\n})\n\ndescribe(\"inferWebAuthnOptions\", () => {\n  const cases: {\n    action?: WebAuthnAction\n    loggedIn: boolean\n    userInfo: Awaited<ReturnType<GetUserInfo>>\n    expected: WebAuthnAction | null\n  }[] = [\n    {\n      action: \"authenticate\",\n      loggedIn: true,\n      userInfo: { user: {}, exists: true },\n      expected: \"authenticate\",\n    },\n    {\n      action: \"authenticate\",\n      loggedIn: false,\n      userInfo: { user: {}, exists: true },\n      expected: \"authenticate\",\n    },\n    {\n      action: \"authenticate\",\n      loggedIn: false,\n      userInfo: null,\n      expected: \"authenticate\",\n    },\n    {\n      action: \"register\",\n      loggedIn: false,\n      userInfo: { user: {}, exists: false },\n      expected: \"register\",\n    },\n    {\n      action: \"register\",\n      loggedIn: true,\n      userInfo: { user: {}, exists: true },\n      expected: \"register\",\n    },\n    {\n      action: \"register\",\n      loggedIn: false,\n      userInfo: null,\n      expected: null,\n    },\n    {\n      action: \"register\",\n      loggedIn: false,\n      userInfo: { user: {}, exists: true },\n      expected: null,\n    },\n    {\n      action: undefined,\n      loggedIn: false,\n      userInfo: { user: {}, exists: true },\n      expected: \"authenticate\",\n    },\n    {\n      action: undefined,\n      loggedIn: false,\n      userInfo: { user: {}, exists: false },\n      expected: \"register\",\n    },\n    {\n      action: undefined,\n      loggedIn: false,\n      userInfo: null,\n      expected: \"authenticate\",\n    },\n    {\n      action: undefined,\n      loggedIn: true,\n      userInfo: { user: {}, exists: true },\n      expected: null,\n    },\n    {\n      action: undefined,\n      loggedIn: true,\n      userInfo: null,\n      expected: null,\n    },\n  ]\n\n  test.each(cases)(\n    \"(%#) ($action, $userInfo, loggedIn: $loggedIn) = $expected\",\n    ({ action, userInfo, expected, loggedIn }) => {\n      expect(inferWebAuthnOptions(action, loggedIn, userInfo)).toEqual(expected)\n    }\n  )\n})\n\ndescribe(\"getRegistrationResponse\", () => {\n  it(\"generates registration response\", async () => {\n    const options = getMockOptions()\n    const user = { id: \"123\", email: \"test@example.com\", name: \"Test User\" }\n    const authenticators = [createAuthenticator(), createAuthenticator()]\n    const returnedOptions = {\n      challenge: \"mychallenge\",\n    } as unknown as PublicKeyCredentialCreationOptionsJSON\n\n    vi.mocked(options.adapter.listAuthenticatorsByUserId).mockResolvedValue(\n      authenticators\n    )\n    vi.mocked(generateRegistrationOptions).mockResolvedValue(returnedOptions)\n\n    const rp = options.provider.getRelayingParty(options, {} as RequestInternal)\n    const expectedOptionsParams: GenerateRegistrationOptionsOpts = {\n      ...defaultWebAuthnConfig.registrationOptions,\n      rpID: rp.id,\n      rpName: rp.name,\n      userID: expect.any(String),\n      userName: user.email,\n      userDisplayName: user.name,\n      excludeCredentials: authenticators.map((a) => ({\n        id: fromBase64(a.credentialID),\n        type: \"public-key\",\n        transports: stringToTransports(a.transports),\n      })),\n    }\n    const cookies = [{ name: \"other\", value: \"value\", options: {} }]\n    const expectedResponse = getExpectedResponse(\n      \"register\",\n      returnedOptions,\n      cookies\n    )\n\n    expect(\n      await getRegistrationResponse(\n        options,\n        {} as RequestInternal,\n        user,\n        cookies\n      )\n    ).toEqual(expectedResponse)\n\n    expect(webauthnChallenge.create).toHaveBeenCalledWith(\n      options,\n      \"mychallenge\",\n      user\n    )\n    expect(generateRegistrationOptions).toHaveBeenCalledWith(\n      expectedOptionsParams\n    )\n    expect(options.adapter.listAuthenticatorsByUserId).toHaveBeenCalledWith(\n      \"123\"\n    )\n  })\n\n  it(\"uses provider override options\", async () => {\n    const options = getMockOptions(\n      {},\n      {\n        registrationOptions: {\n          // @ts-expect-error\n          userID: \"test\",\n          attestationType: \"none\",\n          timeout: 1000,\n          authenticatorSelection: {\n            requireResidentKey: true,\n          },\n          supportedAlgorithmIDs: [1, 2, 3],\n        },\n      }\n    )\n    const user = { id: \"123\", email: \"test@example.com\", name: \"Test User\" }\n    const authenticators = [createAuthenticator(), createAuthenticator()]\n    const returnedOptions = {\n      challenge: \"mychallenge\",\n    } as unknown as PublicKeyCredentialCreationOptionsJSON\n\n    vi.mocked(options.adapter.listAuthenticatorsByUserId).mockResolvedValue(\n      authenticators\n    )\n    vi.mocked(generateRegistrationOptions).mockResolvedValue(returnedOptions)\n\n    const rp = options.provider.getRelayingParty(options, {} as RequestInternal)\n    const expectedOptionsParams: GenerateRegistrationOptionsOpts = {\n      ...defaultWebAuthnConfig.registrationOptions,\n      rpID: rp.id,\n      rpName: rp.name,\n      userID: expect.any(String),\n      userName: user.email,\n      userDisplayName: user.name,\n      excludeCredentials: authenticators.map((a) => ({\n        id: fromBase64(a.credentialID),\n        type: \"public-key\",\n        transports: stringToTransports(a.transports),\n      })),\n      timeout: 1000,\n      attestationType: \"none\",\n      authenticatorSelection: {\n        requireResidentKey: true,\n      },\n      supportedAlgorithmIDs: [1, 2, 3],\n    }\n    const expectedResponse = getExpectedResponse(\"register\", returnedOptions)\n\n    expect(\n      await getRegistrationResponse(options, {} as RequestInternal, user)\n    ).toEqual(expectedResponse)\n\n    expect(webauthnChallenge.create).toHaveBeenCalledWith(\n      options,\n      \"mychallenge\",\n      user\n    )\n    expect(generateRegistrationOptions).toHaveBeenCalledWith(\n      expectedOptionsParams\n    )\n    expect(options.adapter.listAuthenticatorsByUserId).toHaveBeenCalledWith(\n      \"123\"\n    )\n  })\n\n  it(\"doesn't get authenticators for new users\", async () => {\n    const options = getMockOptions()\n    const user = { email: \"test@example.com\", name: \"Test User\" }\n    const returnedOptions = {\n      challenge: \"mychallenge\",\n    } as unknown as PublicKeyCredentialCreationOptionsJSON\n\n    vi.mocked(generateRegistrationOptions).mockResolvedValue(returnedOptions)\n\n    const rp = options.provider.getRelayingParty(options, {} as RequestInternal)\n    const expectedOptionsParams: GenerateRegistrationOptionsOpts = {\n      ...defaultWebAuthnConfig.registrationOptions,\n      rpID: rp.id,\n      rpName: rp.name,\n      userID: expect.any(String),\n      userName: user.email,\n      userDisplayName: user.name,\n      excludeCredentials: undefined,\n    }\n    const expectedResponse = getExpectedResponse(\"register\", returnedOptions)\n\n    expect(\n      await getRegistrationResponse(options, {} as RequestInternal, user)\n    ).toEqual(expectedResponse)\n\n    expect(webauthnChallenge.create).toHaveBeenCalledWith(\n      options,\n      \"mychallenge\",\n      user\n    )\n    expect(generateRegistrationOptions).toHaveBeenCalledWith(\n      expectedOptionsParams\n    )\n    expect(options.adapter.listAuthenticatorsByUserId).not.toHaveBeenCalled()\n  })\n\n  it(\"allows missing userName\", async () => {\n    const options = getMockOptions()\n    const user = { email: \"test@example.com\" }\n    const returnedOptions = {\n      challenge: \"mychallenge\",\n    } as unknown as PublicKeyCredentialCreationOptionsJSON\n\n    vi.mocked(generateRegistrationOptions).mockResolvedValue(returnedOptions)\n\n    const rp = options.provider.getRelayingParty(options, {} as RequestInternal)\n    const expectedOptionsParams: GenerateRegistrationOptionsOpts = {\n      ...defaultWebAuthnConfig.registrationOptions,\n      rpID: rp.id,\n      rpName: rp.name,\n      userID: expect.any(String),\n      userName: user.email,\n      userDisplayName: undefined,\n      excludeCredentials: undefined,\n    }\n    const expectedResponse = getExpectedResponse(\"register\", returnedOptions)\n\n    expect(\n      await getRegistrationResponse(options, {} as RequestInternal, user)\n    ).toEqual(expectedResponse)\n\n    expect(webauthnChallenge.create).toHaveBeenCalledWith(\n      options,\n      \"mychallenge\",\n      user\n    )\n    expect(generateRegistrationOptions).toHaveBeenCalledWith(\n      expectedOptionsParams\n    )\n    expect(options.adapter.listAuthenticatorsByUserId).not.toHaveBeenCalled()\n  })\n})\n\ndescribe(\"getAuthenticationResponse\", () => {\n  it(\"generates authentication response\", async () => {\n    const options = getMockOptions()\n    const user = { id: \"123\", email: \"test@example.com\", name: \"Test User\" }\n    const authenticators = [createAuthenticator(), createAuthenticator()]\n    const returnedOptions = {\n      challenge: \"mychallenge\",\n    } as unknown as PublicKeyCredentialCreationOptionsJSON\n\n    vi.mocked(options.adapter.listAuthenticatorsByUserId).mockResolvedValue(\n      authenticators\n    )\n    vi.mocked(generateAuthenticationOptions).mockResolvedValue(returnedOptions)\n\n    const rp = options.provider.getRelayingParty(options, {} as RequestInternal)\n    const expectedOptionsParams: GenerateAuthenticationOptionsOpts = {\n      ...defaultWebAuthnConfig.authenticationOptions,\n      rpID: rp.id,\n      allowCredentials: authenticators.map((a) => ({\n        id: fromBase64(a.credentialID),\n        type: \"public-key\",\n        transports: stringToTransports(a.transports),\n      })),\n    }\n    const cookies = [{ name: \"other\", value: \"value\", options: {} }]\n    const expectedResponse = getExpectedResponse(\n      \"authenticate\",\n      returnedOptions,\n      cookies\n    )\n\n    expect(\n      await getAuthenticationResponse(\n        options,\n        {} as RequestInternal,\n        user,\n        cookies\n      )\n    ).toEqual(expectedResponse)\n\n    expect(webauthnChallenge.create).toHaveBeenCalledWith(\n      options,\n      \"mychallenge\"\n    )\n    expect(generateAuthenticationOptions).toHaveBeenCalledWith(\n      expectedOptionsParams\n    )\n    expect(options.adapter.listAuthenticatorsByUserId).toHaveBeenCalledWith(\n      \"123\"\n    )\n  })\n\n  it(\"uses provider override options\", async () => {\n    const options = getMockOptions(\n      {},\n      {\n        authenticationOptions: {\n          extensions: {\n            appid: \"test\",\n          },\n          timeout: 1000,\n          userVerification: \"required\",\n        },\n      }\n    )\n    const user = { id: \"123\", email: \"test@example.com\", name: \"Test User\" }\n    const authenticators = [createAuthenticator(), createAuthenticator()]\n    const returnedOptions = {\n      challenge: \"mychallenge\",\n    } as unknown as PublicKeyCredentialCreationOptionsJSON\n\n    vi.mocked(options.adapter.listAuthenticatorsByUserId).mockResolvedValue(\n      authenticators\n    )\n    vi.mocked(generateAuthenticationOptions).mockResolvedValue(returnedOptions)\n\n    const rp = options.provider.getRelayingParty(options, {} as RequestInternal)\n    const expectedOptionsParams: GenerateAuthenticationOptionsOpts = {\n      ...defaultWebAuthnConfig.authenticationOptions,\n      rpID: rp.id,\n      allowCredentials: authenticators.map((a) => ({\n        id: fromBase64(a.credentialID),\n        type: \"public-key\",\n        transports: stringToTransports(a.transports),\n      })),\n      timeout: 1000,\n      userVerification: \"required\",\n      extensions: {\n        appid: \"test\",\n      },\n    }\n    const cookies = [{ name: \"other\", value: \"value\", options: {} }]\n    const expectedResponse = getExpectedResponse(\n      \"authenticate\",\n      returnedOptions,\n      cookies\n    )\n\n    expect(\n      await getAuthenticationResponse(\n        options,\n        {} as RequestInternal,\n        user,\n        cookies\n      )\n    ).toEqual(expectedResponse)\n\n    expect(webauthnChallenge.create).toHaveBeenCalledWith(\n      options,\n      \"mychallenge\"\n    )\n    expect(generateAuthenticationOptions).toHaveBeenCalledWith(\n      expectedOptionsParams\n    )\n    expect(options.adapter.listAuthenticatorsByUserId).toHaveBeenCalledWith(\n      \"123\"\n    )\n  })\n\n  it(\"accepts undefined user\", async () => {\n    const options = getMockOptions()\n    const user = undefined\n    const returnedOptions = {\n      challenge: \"mychallenge\",\n    } as unknown as PublicKeyCredentialCreationOptionsJSON\n\n    vi.mocked(generateAuthenticationOptions).mockResolvedValue(returnedOptions)\n\n    const rp = options.provider.getRelayingParty(options, {} as RequestInternal)\n    const expectedOptionsParams: GenerateAuthenticationOptionsOpts = {\n      ...defaultWebAuthnConfig.authenticationOptions,\n      rpID: rp.id,\n      allowCredentials: undefined,\n    }\n    const cookies = [{ name: \"other\", value: \"value\", options: {} }]\n    const expectedResponse = getExpectedResponse(\n      \"authenticate\",\n      returnedOptions,\n      cookies\n    )\n\n    expect(\n      await getAuthenticationResponse(\n        options,\n        {} as RequestInternal,\n        user,\n        cookies\n      )\n    ).toEqual(expectedResponse)\n\n    expect(webauthnChallenge.create).toHaveBeenCalledWith(\n      options,\n      \"mychallenge\"\n    )\n    expect(generateAuthenticationOptions).toHaveBeenCalledWith(\n      expectedOptionsParams\n    )\n    expect(options.adapter.listAuthenticatorsByUserId).not.toHaveBeenCalled()\n  })\n})\n\ndescribe(\"verifyAuthenticate\", () => {\n  it(\"verifies authentication response\", async () => {\n    const {\n      account,\n      user,\n      options,\n      request,\n      cookies,\n      credentialID,\n      newCounter,\n      expectedAuthenticationResponse,\n    } = prepareVerifyTest(\"authenticate\")\n\n    expect(await verifyAuthenticate(options, request, cookies)).toEqual({\n      account,\n      user,\n    })\n\n    expect(options.adapter.getAuthenticator).toHaveBeenCalledWith(credentialID)\n    expect(webauthnChallenge.use).toHaveBeenCalledWith(\n      options,\n      \"reqcookies\",\n      cookies\n    )\n    expect(verifyAuthenticationResponse).toHaveBeenCalledWith(\n      expectedAuthenticationResponse\n    )\n    expect(options.adapter.updateAuthenticatorCounter).toHaveBeenCalledWith(\n      credentialID,\n      newCounter\n    )\n    expect(options.adapter.getAccount).toHaveBeenCalledWith(\n      credentialID,\n      \"webauthn\"\n    )\n    expect(options.adapter.getUser).toHaveBeenCalledWith(user.id)\n  })\n\n  it(\"provider overrides verify options\", async () => {\n    const {\n      account,\n      user,\n      options,\n      request,\n      cookies,\n      expectedAuthenticationResponse,\n    } = prepareVerifyTest(\"authenticate\")\n    options.provider.verifyAuthenticationOptions = {\n      ...options.provider.verifyAuthenticationOptions,\n      expectedType: \"public-key\",\n      requireUserVerification: true,\n    }\n\n    expect(await verifyAuthenticate(options, request, cookies)).toEqual({\n      account,\n      user,\n    })\n\n    expect(verifyAuthenticationResponse).toHaveBeenCalledWith({\n      ...expectedAuthenticationResponse,\n      expectedType: \"public-key\",\n      requireUserVerification: true,\n    })\n  })\n\n  it(\"errors on invalid request body\", async () => {\n    const { options, request, cookies } = prepareVerifyTest(\"authenticate\", {\n      key: \"value\",\n    })\n\n    await expect(() =>\n      verifyAuthenticate(options, request, cookies)\n    ).rejects.toThrowErrorMatchingInlineSnapshot(\n      `[AuthError: Invalid WebAuthn Authentication response. Read more at https://errors.authjs.dev#autherror]`\n    )\n\n    expect(webauthnChallenge.use).not.toHaveBeenCalled()\n    expect(options.adapter.updateAuthenticatorCounter).not.toHaveBeenCalled()\n  })\n\n  it(\"errors on invalid authenticator\", async () => {\n    const { options, request, cookies, credentialID } =\n      prepareVerifyTest(\"authenticate\")\n\n    vi.mocked(options.adapter.getAuthenticator).mockResolvedValue(null)\n\n    await expect(() =>\n      verifyAuthenticate(options, request, cookies)\n    ).rejects.toThrowErrorMatchingInlineSnapshot(\n      `[AuthError: WebAuthn authenticator not found in database: {\"credentialID\":\"AQIDBAU=\"}. Read more at https://errors.authjs.dev#autherror]`\n    )\n\n    expect(options.adapter.getAuthenticator).toHaveBeenCalledWith(credentialID)\n    expect(webauthnChallenge.use).not.toHaveBeenCalled()\n    expect(options.adapter.updateAuthenticatorCounter).not.toHaveBeenCalled()\n  })\n\n  it(\"errors on failed response verification\", async () => {\n    const { options, request, cookies, expectedAuthenticationResponse } =\n      prepareVerifyTest(\"authenticate\")\n\n    vi.mocked(verifyAuthenticationResponse).mockRejectedValue(\n      new Error(\"mytesterror\")\n    )\n\n    await expect(() =>\n      verifyAuthenticate(options, request, cookies)\n    ).rejects.toThrow(WebAuthnVerificationError)\n\n    expect(verifyAuthenticationResponse).toHaveBeenCalledWith(\n      expectedAuthenticationResponse\n    )\n    expect(options.adapter.updateAuthenticatorCounter).not.toHaveBeenCalled()\n  })\n\n  it(\"errors on failed verification verified\", async () => {\n    const { options, request, cookies, expectedAuthenticationResponse } =\n      prepareVerifyTest(\"authenticate\")\n\n    // @ts-expect-error\n    vi.mocked(verifyAuthenticationResponse).mockResolvedValue({\n      verified: false,\n    })\n\n    await expect(() =>\n      verifyAuthenticate(options, request, cookies)\n    ).rejects.toThrow(WebAuthnVerificationError)\n\n    expect(verifyAuthenticationResponse).toHaveBeenCalledWith(\n      expectedAuthenticationResponse\n    )\n    expect(options.adapter.updateAuthenticatorCounter).not.toHaveBeenCalled()\n  })\n\n  it(\"errors if authenticator update fails\", async () => {\n    const { options, request, cookies, credentialID, newCounter } =\n      prepareVerifyTest(\"authenticate\")\n\n    vi.mocked(options.adapter.updateAuthenticatorCounter).mockRejectedValue(\n      new Error(\"mytesterror\")\n    )\n\n    await expect(() =>\n      verifyAuthenticate(options, request, cookies)\n    ).rejects.toThrowError(AdapterError)\n\n    expect(options.adapter.updateAuthenticatorCounter).toHaveBeenCalledWith(\n      credentialID,\n      newCounter\n    )\n  })\n\n  it(\"errors if account does not exist\", async () => {\n    const { options, request, cookies, credentialID } =\n      prepareVerifyTest(\"authenticate\")\n\n    vi.mocked(options.adapter.getAccount).mockResolvedValue(null)\n\n    await expect(() =>\n      verifyAuthenticate(options, request, cookies)\n    ).rejects.toThrowErrorMatchingInlineSnapshot(\n      `[AuthError: WebAuthn account not found in database: {\"credentialID\":\"AQIDBAU=\",\"providerAccountId\":\"AQIDBAU=\"}. Read more at https://errors.authjs.dev#autherror]`\n    )\n\n    expect(options.adapter.getAccount).toHaveBeenCalledWith(\n      credentialID,\n      \"webauthn\"\n    )\n  })\n\n  it(\"errors if user does not exist\", async () => {\n    const { options, request, cookies, user } =\n      prepareVerifyTest(\"authenticate\")\n\n    vi.mocked(options.adapter.getUser).mockResolvedValue(null)\n\n    await expect(() =>\n      verifyAuthenticate(options, request, cookies)\n    ).rejects.toThrow(AuthError)\n\n    expect(options.adapter.getUser).toHaveBeenCalledWith(user.id)\n  })\n})\n\ndescribe(\"verifyRegister\", () => {\n  it(\"verifies registration response\", async () => {\n    const {\n      account: { userId, ...account },\n      user: { id, ...user },\n      options,\n      request,\n      cookies,\n      authenticator: { userId: auid, ...authenticator },\n      expectedRegistrationResponse,\n    } = prepareVerifyTest(\"register\")\n    vi.mocked(webauthnChallenge.use).mockResolvedValue({\n      challenge: \"mychallenge\",\n      registerData: user,\n    })\n\n    expect(await verifyRegister(options, request, cookies)).toEqual({\n      account,\n      user,\n      authenticator,\n    })\n\n    expect(webauthnChallenge.use).toHaveBeenCalledWith(\n      options,\n      request.cookies,\n      cookies\n    )\n    expect(verifyRegistrationResponse).toHaveBeenCalledWith(\n      expectedRegistrationResponse\n    )\n  })\n\n  it(\"provider overrides verification options\", async () => {\n    const {\n      account: { userId, ...account },\n      user: { id, ...user },\n      options,\n      request,\n      cookies,\n      authenticator: { userId: auid, ...authenticator },\n      expectedRegistrationResponse,\n    } = prepareVerifyTest(\"register\")\n    vi.mocked(webauthnChallenge.use).mockResolvedValue({\n      challenge: \"mychallenge\",\n      registerData: user,\n    })\n    options.provider.verifyRegistrationOptions = {\n      ...options.provider.verifyRegistrationOptions,\n      expectedType: \"public-key\",\n      requireUserVerification: true,\n      supportedAlgorithmIDs: [1, 2, 3],\n    }\n\n    expect(await verifyRegister(options, request, cookies)).toEqual({\n      account,\n      user,\n      authenticator,\n    })\n\n    expect(verifyRegistrationResponse).toHaveBeenCalledWith({\n      ...expectedRegistrationResponse,\n      expectedType: \"public-key\",\n      requireUserVerification: true,\n      supportedAlgorithmIDs: [1, 2, 3],\n    })\n  })\n\n  it(\"fails on invalid request body\", async () => {\n    const {\n      user: { id, ...user },\n      options,\n      request,\n      cookies,\n    } = prepareVerifyTest(\"register\", { key: \"value\" })\n\n    await expect(() =>\n      verifyRegister(options, request, cookies)\n    ).rejects.toThrowErrorMatchingInlineSnapshot(\n      `[AuthError: Invalid WebAuthn Registration response. Read more at https://errors.authjs.dev#autherror]`\n    )\n\n    expect(webauthnChallenge.use).not.toHaveBeenCalled()\n    expect(verifyRegistrationResponse).not.toHaveBeenCalled()\n  })\n\n  it(\"errors on missing registration data in challenge cookie\", async () => {\n    const { options, request, cookies } = prepareVerifyTest(\"register\")\n\n    await expect(() =>\n      verifyRegister(options, request, cookies)\n    ).rejects.toThrowErrorMatchingInlineSnapshot(\n      `[AuthError: Missing user registration data in WebAuthn challenge cookie. Read more at https://errors.authjs.dev#autherror]`\n    )\n\n    expect(webauthnChallenge.use).toHaveBeenCalledWith(\n      options,\n      request.cookies,\n      cookies\n    )\n    expect(verifyRegistrationResponse).not.toHaveBeenCalled()\n  })\n\n  it(\"errors on failed verification\", async () => {\n    const {\n      account: { userId, ...account },\n      user: { id, ...user },\n      options,\n      request,\n      cookies,\n      authenticator: { userId: auid, ...authenticator },\n      expectedRegistrationResponse,\n    } = prepareVerifyTest(\"register\")\n    vi.mocked(webauthnChallenge.use).mockResolvedValue({\n      challenge: \"mychallenge\",\n      registerData: user,\n    })\n    vi.mocked(verifyRegistrationResponse).mockRejectedValue(\n      new Error(\"mytesterror\")\n    )\n\n    await expect(() =>\n      verifyRegister(options, request, cookies)\n    ).rejects.toThrow(WebAuthnVerificationError)\n\n    expect(webauthnChallenge.use).toHaveBeenCalledWith(\n      options,\n      request.cookies,\n      cookies\n    )\n    expect(verifyRegistrationResponse).toHaveBeenCalledWith(\n      expectedRegistrationResponse\n    )\n  })\n\n  it(\"errors on failed verification verified\", async () => {\n    const {\n      account: { userId, ...account },\n      user: { id, ...user },\n      options,\n      request,\n      cookies,\n      authenticator: { userId: auid, ...authenticator },\n      expectedRegistrationResponse,\n    } = prepareVerifyTest(\"register\")\n    vi.mocked(webauthnChallenge.use).mockResolvedValue({\n      challenge: \"mychallenge\",\n      registerData: user,\n    })\n    vi.mocked(verifyRegistrationResponse).mockResolvedValue({ verified: false })\n\n    await expect(() =>\n      verifyRegister(options, request, cookies)\n    ).rejects.toThrow(WebAuthnVerificationError)\n\n    expect(webauthnChallenge.use).toHaveBeenCalledWith(\n      options,\n      request.cookies,\n      cookies\n    )\n    expect(verifyRegistrationResponse).toHaveBeenCalledWith(\n      expectedRegistrationResponse\n    )\n  })\n})\n"
  },
  {
    "path": "packages/core/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"preact\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"outDir\": \".\",\n    \"rootDir\": \"src\",\n    \"declaration\": true,\n    \"declarationMap\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"*.js\", \"*.d.ts\", \"lib\", \"providers\"]\n}\n"
  },
  {
    "path": "packages/core/typedoc.config.cjs",
    "content": "// @ts-check\nconst fs = require(\"node:fs\")\nconst path = require(\"node:path\")\n\nconst providers = fs\n  .readdirSync(path.join(__dirname, \"src\", \"providers\"))\n  .filter((file) => file.endsWith(\".ts\") && !file.startsWith(\"oauth\"))\n  .map((p) => `src/providers/${p}`)\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').PluginOptions}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\", \"src/adapters.ts\", \"src/errors.ts\", \"src/jwt.ts\", \"src/types.ts\"].concat(providers),\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryFileName: \"../core.mdx\",\n  entryModule: \"@auth/core\",\n  includeVersion: true,  \n  readme:'none',\n}\n"
  },
  {
    "path": "packages/frameworks-express/README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://expressjs.com\" target=\"_blank\"><img height=\"96px\" src=\"https://authjs.dev/img/etc/express.svg\" /></a>\n  <a href=\"https://express.authjs.dev\" target=\"_blank\"><img height=\"96px\" src=\"https://authjs.dev/img/logo-sm.png\" /></a>\n  <h1 align=\"center\">Express Auth</h1>\n</p>\n<p align=\"center\">\n  Authentication for Express.\n</p>\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@auth/express\"><img src=\"https://img.shields.io/npm/v/@auth/express?style=flat-square&label=latest&color=purple\" alt=\"npm latest release\" /></a>\n  <a href=\"https://www.npmtrends.com/@auth/express\"><img src=\"https://img.shields.io/npm/dm/@auth/express?style=flat-square&color=cyan\" alt=\"Downloads\" /></a>\n  <a href=\"https://github.com/nextauthjs/next-auth/stargazers\"><img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square&color=orange\" alt=\"GitHub Stars\" /></a>\n  <img src=\"https://shields.io/badge/TypeScript-3178C6?logo=TypeScript&logoColor=fff&style=flat-square\" alt=\"TypeScript\" />\n</p>\n\n---\n\nCheck out the documentation at [express.authjs.dev](https://express.authjs.dev).\n"
  },
  {
    "path": "packages/frameworks-express/package.json",
    "content": "{\n  \"name\": \"@auth/express\",\n  \"description\": \"Authentication for Express.\",\n  \"version\": \"0.12.0\",\n  \"type\": \"module\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"lib\",\n    \"providers\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\",\n      \"default\": \"./index.js\"\n    },\n    \"./providers\": {\n      \"types\": \"./providers/index.d.ts\"\n    },\n    \"./adapters\": {\n      \"types\": \"./adapters.d.ts\"\n    },\n    \"./providers/*\": {\n      \"types\": \"./providers/*.d.ts\",\n      \"import\": \"./providers/*.js\",\n      \"default\": \"./providers/*.js\"\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"scripts\": {\n    \"build\": \"pnpm clean && pnpm providers && tsc\",\n    \"clean\": \"rm -rf lib *.js *.d.ts* src/lib/providers\",\n    \"test\": \"vitest run -c ../utils/vitest.config.ts\",\n    \"providers\": \"node ../utils/scripts/providers\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"@auth/core\": \"workspace:*\",\n    \"@types/express\": \"^4.17.17\",\n    \"@types/supertest\": \"^2.0.12\",\n    \"supertest\": \"^6.3.3\"\n  },\n  \"peerDependencies\": {\n    \"express\": \"^4.18.2 || ^5.0.0\"\n  },\n  \"keywords\": [\n    \"Express\",\n    \"Auth.js\"\n  ],\n  \"author\": \"Rexford Essilfie <rexfordessilfie09@gmail.com>\",\n  \"contributors\": [\n    \"Rexford Essilfie <rexfordessilfie09@gmail.com>\",\n    \"Lachie Hill <lachiehill@gmail.com>\"\n  ],\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"license\": \"ISC\"\n}\n"
  },
  {
    "path": "packages/frameworks-express/src/adapters.ts",
    "content": "export type * from \"@auth/core/adapters\"\n"
  },
  {
    "path": "packages/frameworks-express/src/index.ts",
    "content": "/**\n *\n * :::warning\n * `@auth/express` is currently experimental. The API _will_ change in the future.\n * :::\n *\n * Express Auth is the official Express integration for Auth.js.\n * It provides a simple way to add authentication to your Express app in a few lines of code.\n *\n * ## Installation\n * ```bash npm2yarn\n * npm install @auth/express\n * ```\n *\n * ## Usage\n *\n * ```ts title=\"src/routes/auth.route.ts\"\n * import { ExpressAuth } from \"@auth/express\"\n * import GitHub from \"@auth/express/providers/github\"\n * import express from \"express\"\n *\n * const app = express()\n *\n * // If app is served through a proxy, trust the proxy to allow HTTPS protocol to be detected\n * // https://expressjs.com/en/guide/behind-proxies.html\n * app.set('trust proxy', true)\n * app.use(\"/auth/*\", ExpressAuth({ providers: [ GitHub ] }))\n * ```\n *\n * Don't forget to set the `AUTH_SECRET` environment variable. This should be a minimum of 32 characters, random string. On UNIX systems you can use `openssl rand -hex 32` or check out `https://generate-secret.vercel.app/32`.\n *\n * You will also need to load the environment variables into your runtime environment. For example in Node.js with a package like [`dotenv`](https://www.npmjs.com/package/dotenv) or `Deno.env` in Deno.\n *\n * ### Provider Configuration\n * The callback URL used by the [providers](https://authjs.dev/reference/core/providers) must be set to the following, unless you mount the `ExpressAuth` handler on a different path:\n *\n * ```\n * [origin]/auth/callback/[provider]\n * ```\n *\n * ## Signing in and signing out\n * Once your application is mounted you can sign in or out by making requests to the following [REST API endpoints](https://authjs.dev/reference/core/types#authaction) from your client-side code.\n * NB: Make sure to include the `csrfToken` in the request body for all sign-in and sign-out requests.\n *\n * ## Managing the session\n * If you are using Express with a template engine (e.g EJS, Pug), you can make the session data available to all routes via middleware as follows\n *\n * ```ts title=\"app.ts\"\n * import { getSession } from \"@auth/express\"\n *\n * export function authSession(req: Request, res: Response, next: NextFunction) {\n *   res.locals.session = await getSession(req)\n *   next()\n * }\n *\n * app.use(authSession)\n *\n * // Now in your route\n * app.get(\"/\", (req, res) => {\n *   const { session } = res.locals\n *   res.render(\"index\", { user: session?.user })\n * })\n * ```\n *\n * ## Authorization\n * You can protect routes by checking for the presence of a session and then redirect to a login page if the session is not present.\n * This can either be done per route, or for a group of routes using a middleware such as the following:\n *\n * ```ts\n * export async function authenticatedUser(\n *   req: Request,\n *   res: Response,\n *   next: NextFunction\n * ) {\n *   const session = res.locals.session ?? (await getSession(req, authConfig))\n *   if (!session?.user) {\n *     res.redirect(\"/login\")\n *   } else {\n *     next()\n *   }\n * }\n * ```\n *\n * ### Per Route\n * To protect a single route, simply add the middleware to the route as follows:\n * ```ts title=\"app.ts\"\n * // This route is protected\n * app.get(\"/profile\", authenticatedUser, (req, res) => {\n *   const { session } = res.locals\n *   res.render(\"profile\", { user: session?.user })\n * })\n *\n * // This route is not protected\n * app.get(\"/\", (req, res) => {\n *   res.render(\"index\")\n * })\n *\n * app.use(\"/\", root)\n * ```\n * ### Per Group of Routes\n * To protect a group of routes, define a router and add the middleware to the router as follows:\n *\n * ```ts title=\"routes/protected.route.ts\"\n * import { Router } from \"express\"\n *\n * const router = Router()\n *\n * router.use(authenticatedUser) // All routes defined after this will be protected\n *\n * router.get(\"/\", (req, res) => {\n *   res.render(\"protected\")\n * })\n *\n * export default router\n * ```\n *\n * Then we mount the router as follows:\n * ```ts title=\"app.ts\"\n * import protected from \"./routes/protected.route\"\n *\n * app.use(\"/protected\", protected)\n * ```\n *\n * ## Notes on ESM\n * @auth/express is ESM only. This means your package.json must contain `\"type\": \"module\"` and tsconfig.json should contain `\"module\": \"NodeNext\"` or `ESNext`.\n * File imports must use the `.js` extension, e.g. `import { MyRouter } from \"./my-router.js\"`.\n *\n * Your dev server should either be run with [tsx](https://www.npmjs.com/package/tsx) with `tsx index.ts` (fast startup, with no type checking), or ts-node with 'node --loader ts-node/esm index.ts' (slower startup, but has type checking).\n *\n * While it is NOT recommended, if you wish to use @auth/express within a CommonJS project without migrating and making the above changes, you can run the dev server with tsx and may be able to compile with [pkgroll](https://tsx.is/compilation).\n * Add '\"name\": \"./dist/index.js\"' or '\"name\": \"./dist/index.mjs\"' to your package.json and run 'pkgroll' to compile with both ESM and CommonJS support. For new projects it is recommended to just use ESM.\n *\n * @module @auth/express\n */\n\nimport {\n  Auth,\n  type AuthConfig,\n  setEnvDefaults,\n  createActionURL,\n  customFetch,\n} from \"@auth/core\"\nimport type { Session } from \"@auth/core/types\"\nimport * as e from \"express\"\nimport { toWebRequest, toExpressResponse } from \"./lib/index.js\"\n\nexport { customFetch }\nexport { AuthError, CredentialsSignin } from \"@auth/core/errors\"\nexport type {\n  Account,\n  DefaultSession,\n  Profile,\n  Session,\n  User,\n} from \"@auth/core/types\"\n\nexport type ExpressAuthConfig = Omit<AuthConfig, \"raw\">\n\nexport function ExpressAuth(config: ExpressAuthConfig) {\n  return async (req: e.Request, res: e.Response, next: e.NextFunction) => {\n    e.json()(req, res, async (err) => {\n      if (err) return next(err)\n      e.urlencoded({ extended: true })(req, res, async (err) => {\n        if (err) return next(err)\n        try {\n          config.basePath = getBasePath(req)\n          setEnvDefaults(process.env, config)\n          await toExpressResponse(await Auth(toWebRequest(req), config), res)\n          if (!res.headersSent) next()\n        } catch (error) {\n          next(error)\n        }\n      })\n    })\n  }\n}\n\nexport type GetSessionResult = Promise<Session | null>\n\nexport async function getSession(\n  req: e.Request,\n  config: ExpressAuthConfig\n): GetSessionResult {\n  setEnvDefaults(process.env, config)\n  const url = createActionURL(\n    \"session\",\n    req.protocol,\n    // @ts-expect-error\n    new Headers(req.headers),\n    process.env,\n    config\n  )\n\n  const response = await Auth(\n    new Request(url, { headers: { cookie: req.headers.cookie ?? \"\" } }),\n    config\n  )\n\n  const { status = 200 } = response\n\n  const data = await response.json()\n\n  if (!data || !Object.keys(data).length) return null\n  if (status === 200) return data\n  throw new Error(data.message)\n}\n\nfunction getBasePath(req: e.Request) {\n  return req.baseUrl.split(req.params[0])[0].replace(/\\/$/, \"\")\n}\n"
  },
  {
    "path": "packages/frameworks-express/src/lib/http-api-adapters.ts",
    "content": "import { Request as ExpressRequest, Response as ExpressResponse } from \"express\"\n\n/**\n * Encodes an object as url-encoded string.\n */\nexport function encodeUrlEncoded(object: Record<string, any> = {}) {\n  const params = new URLSearchParams()\n\n  for (const [key, value] of Object.entries(object)) {\n    if (Array.isArray(value)) {\n      value.forEach((v) => params.append(key, v))\n    } else {\n      params.append(key, value)\n    }\n  }\n\n  return params.toString()\n}\n\n/**\n * Encodes an object as JSON\n */\nfunction encodeJson(obj: Record<string, any>) {\n  return JSON.stringify(obj)\n}\n\n/**\n * Encodes an Express Request body based on the content type header.\n */\nfunction encodeRequestBody(req: ExpressRequest) {\n  const contentType = req.headers[\"content-type\"]\n\n  if (contentType?.includes(\"application/x-www-form-urlencoded\")) {\n    return encodeUrlEncoded(req.body)\n  }\n\n  if (contentType?.includes(\"application/json\")) {\n    return encodeJson(req.body)\n  }\n\n  return req.body\n}\n\n/**\n * Adapts an Express Request to a Web Request, returning the Web Request.\n */\nexport function toWebRequest(req: ExpressRequest) {\n  const url = req.protocol + \"://\" + req.get(\"host\") + req.originalUrl\n\n  const headers = new Headers()\n\n  Object.entries(req.headers).forEach(([key, value]) => {\n    if (Array.isArray(value)) {\n      value.forEach((v) => v && headers.append(key, v))\n      return\n    }\n\n    if (value) {\n      headers.append(key, value)\n    }\n  })\n\n  // GET and HEAD not allowed to receive body\n  const body = /GET|HEAD/.test(req.method) ? undefined : encodeRequestBody(req)\n\n  const request = new Request(url, {\n    method: req.method,\n    headers,\n    body,\n  })\n\n  return request\n}\n\n/**\n * Adapts a Web Response to an Express Response, invoking appropriate\n * Express response methods to handle the response.\n */\nexport async function toExpressResponse(\n  response: Response,\n  res: ExpressResponse\n) {\n  response.headers.forEach((value, key) => {\n    if (value) {\n      res.append(key, value)\n    }\n  })\n\n  // Explicitly write the headers for content-type\n  // https://stackoverflow.com/a/59449326/13944042\n  res.writeHead(response.status, response.statusText, {\n    \"Content-Type\": response.headers.get(\"content-type\") || \"\",\n  })\n\n  res.write(await response.text())\n  res.end()\n}\n"
  },
  {
    "path": "packages/frameworks-express/src/lib/index.ts",
    "content": "export * from \"./http-api-adapters.js\"\n"
  },
  {
    "path": "packages/frameworks-express/test/http-api-adapters/request.test.ts",
    "content": "import { describe, beforeEach, it, expect } from \"vitest\"\nimport { encodeUrlEncoded, toWebRequest } from \"../../src/lib\"\nimport { Request as ExpressRequest } from \"express\"\nimport supertest from \"supertest\"\nimport express from \"express\"\n\nfunction expectMatchingRequestHeaders(req: ExpressRequest, request: Request) {\n  for (let headerName in req.headers) {\n    expect(request.headers.get(headerName)).toEqual(req.headers[headerName])\n  }\n}\n\nasync function expectMatchingJsonRequestBody(\n  req: ExpressRequest,\n  request: Request\n) {\n  const body = await request.json()\n  expect(body).toEqual(req.body)\n}\n\nasync function expectMatchingUrlEncodedRequestBody(\n  req: ExpressRequest,\n  request: Request\n) {\n  const body = await request.text()\n  expect(body).toEqual(encodeUrlEncoded(req.body))\n}\n\ndescribe(\"toWebRequest\", () => {\n  let app: express.Express\n  let client: ReturnType<typeof supertest>\n\n  beforeEach(() => {\n    app = express()\n    client = supertest(app)\n  })\n\n  it(\"adapts request headers\", async () => {\n    let expectations: Function = () => {}\n\n    app.use(express.json())\n\n    app.post(\"/\", async (req, res) => {\n      const request = toWebRequest(req)\n\n      expectations = async () => {\n        expectMatchingRequestHeaders(req, request)\n      }\n\n      res.send(\"OK\")\n    })\n\n    await client\n      .post(\"/\")\n      .set(\"X-Test-Header\", \"foo\")\n      .set(\"Accept\", \"application/json\")\n\n    await expectations()\n  })\n\n  it(\"adapts request with json encoded body\", async () => {\n    let expectations: Function = () => {}\n\n    app.use(express.json())\n\n    app.post(\"/\", async (req, res) => {\n      const request = toWebRequest(req)\n\n      expectations = async () => {\n        await expectMatchingJsonRequestBody(req, request)\n      }\n\n      res.send(\"OK\")\n    })\n\n    const data = {\n      name: \"Rexford\",\n    }\n\n    await client.post(\"/\").set(\"Content-Type\", \"application/json\").send(data)\n\n    await expectations()\n  })\n\n  it(\"adapts request with url-encoded body\", async () => {\n    let expectations: Function = () => {}\n\n    app.use(express.urlencoded())\n\n    app.post(\"/\", async (req, res) => {\n      const request = toWebRequest(req)\n\n      expectations = async () => {\n        await expectMatchingUrlEncodedRequestBody(req, request)\n      }\n\n      res.send(\"OK\")\n    })\n\n    const data = {\n      name: \"Rexford\",\n      nums: [1, 2, 3],\n    }\n\n    await client\n      .post(\"/\")\n      .set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n      .send(encodeUrlEncoded(data))\n\n    await expectations()\n  })\n})\n"
  },
  {
    "path": "packages/frameworks-express/test/http-api-adapters/response.test.ts",
    "content": "import { describe, beforeEach, it, expect } from \"vitest\"\nimport supertest from \"supertest\"\nimport express from \"express\"\nimport { toExpressResponse } from \"../../src/lib\"\n\nfunction expectMatchingResponseHeaders(\n  response: Response,\n  res: supertest.Response\n) {\n  for (let [headerName] of response.headers) {\n    expect(response.headers.get(headerName)).toEqual(\n      res.headers[headerName.toLowerCase()]\n    )\n  }\n}\n\ndescribe(\"toWebResponse\", () => {\n  let app: express.Express\n  let client: ReturnType<typeof supertest>\n\n  beforeEach(() => {\n    app = express()\n    client = supertest(app)\n  })\n\n  it(\"adapts response\", async () => {\n    let webResponse: Response = new Response()\n\n    app.post(\"/\", async (req, res) => {\n      const headers = new Headers()\n      headers.append(\"X-Test-Header\", \"foo\")\n      headers.append(\"Content-Type\", \"application/json\")\n\n      webResponse = new Response(JSON.stringify({ name: \"Rexford\" }), {\n        headers: headers,\n        status: 200,\n      })\n      toExpressResponse(webResponse, res)\n    })\n\n    const res = await client.post(\"/\")\n\n    expectMatchingResponseHeaders(webResponse, res)\n    expect(res.body).toEqual({ name: \"Rexford\" })\n  })\n})\n"
  },
  {
    "path": "packages/frameworks-express/test/login.test.ts",
    "content": "import { describe, beforeEach, it, expect } from \"vitest\"\nimport supertest from \"supertest\"\nimport express from \"express\"\nimport { ExpressAuth, getSession } from \"../src/index.js\"\n\nimport CredentialsProvider from \"@auth/core/providers/credentials\"\nimport type { AuthConfig } from \"@auth/core\"\n\nexport const authConfig = {\n  secret: \"secret\",\n  providers: [\n    CredentialsProvider({\n      credentials: { username: { label: \"Username\" } },\n      async authorize(credentials) {\n        if (typeof credentials?.username === \"string\") {\n          const { username: name } = credentials\n          return { name: name, email: name.replace(\" \", \"\") + \"@example.com\" }\n        }\n        return null\n      },\n    }),\n  ],\n} satisfies AuthConfig\n\nconst extractCookieValue = (cookieHeader: string | string[], name: string) => {\n  const cookieStringFull = Array.isArray(cookieHeader)\n    ? cookieHeader.find((header) => header.includes(name))\n    : cookieHeader\n  return name + cookieStringFull?.split(name)[1].split(\";\")[0]\n}\n\ndescribe(\"Integration test with login and getSession\", () => {\n  let app: express.Express\n  let client: ReturnType<typeof supertest>\n\n  beforeEach(() => {\n    app = express()\n    client = supertest(app)\n  })\n\n  it(\"Should return the session with username after logging in\", async () => {\n    let expectations = () => {}\n\n    app.use(\"/auth/*\", ExpressAuth(authConfig))\n\n    app.post(\"/test\", async (req, res) => {\n      const session = await getSession(req, authConfig)\n\n      expectations = async () => {\n        expect(session?.user?.name).toEqual(\"johnsmith\")\n      }\n\n      res.send(\"OK\")\n    })\n\n    // Get signin page\n    const response = await client\n      .get(\"/auth/signin\")\n      .set(\"X-Test-Header\", \"foo\")\n      .set(\"Accept\", \"application/json\")\n\n    // Parse cookies for csrf token and callback url\n    const csrfTokenCookie = extractCookieValue(\n      response.headers[\"set-cookie\"],\n      \"authjs.csrf-token\"\n    )\n    const callbackCookie = extractCookieValue(\n      response.headers[\"set-cookie\"],\n      \"authjs.callback-url\"\n    )\n    const csrfTokenValue = csrfTokenCookie.split(\"%\")[0].split(\"=\")[1]\n\n    // Sign in\n    const responseCredentials = await client\n      .post(\"/auth/callback/credentials\")\n      .set(\"Cookie\", [csrfTokenCookie, callbackCookie]) // Send the cookie with the request\n      .send({ csrfToken: csrfTokenValue, username: \"johnsmith\" })\n\n    // Parse cookie for session token\n    const sessionTokenCookie = extractCookieValue(\n      responseCredentials.headers[\"set-cookie\"],\n      \"authjs.session-token\"\n    )\n\n    // Call test route\n    await client\n      .post(\"/test\")\n      .set(\"X-Test-Header\", \"foo\")\n      .set(\"Accept\", \"application/json\")\n      .set(\"Cookie\", [sessionTokenCookie])\n\n    await expectations()\n  })\n})\n"
  },
  {
    "path": "packages/frameworks-express/test/routing.test.ts",
    "content": "import { describe, beforeEach, it, expect, vi } from \"vitest\"\nimport supertest from \"supertest\"\nimport express from \"express\"\nimport { ExpressAuth } from \"../src/index.js\"\n\nimport CredentialsProvider from \"@auth/core/providers/credentials\"\nimport type { AuthConfig } from \"@auth/core\"\n\n// mock the toWebRequest, make it throw if \"X-Test-Header\" = 'throw'\nvi.mock(\"../src/lib/index.js\", async (importOriginal) => {\n  const mod = await importOriginal<typeof import(\"../src/lib/index.js\")>()\n  return {\n    ...mod,\n    toWebRequest: vi.fn((req) => {\n      if (req.headers[\"x-test-header\"] === \"throw\") {\n        throw new Error(\"Test error\")\n      }\n      return mod.toWebRequest(req)\n    }),\n  }\n})\n\nexport const authConfig = {\n  secret: \"secret\",\n  providers: [\n    CredentialsProvider({\n      credentials: { username: { label: \"Username\" } },\n      async authorize(credentials) {\n        if (typeof credentials?.username === \"string\") {\n          const { username: name } = credentials\n          return { name: name, email: name.replace(\" \", \"\") + \"@example.com\" }\n        }\n        return null\n      },\n    }),\n  ],\n} satisfies AuthConfig\n\ndescribe(\"Middleware behaviour\", () => {\n  let app: express.Express\n  let client: ReturnType<typeof supertest>\n  let error: Error | null\n\n  beforeEach(() => {\n    app = express()\n    client = supertest(app)\n\n    error = null\n\n    app.use(\"/auth/*\", ExpressAuth(authConfig))\n    app.get(\"/*\", (req, res, next) => {\n      try {\n        res.send(\"Hello World\")\n      } catch (err) {\n        error = err\n      }\n    })\n    app.use((err, req, res, next) => {\n      error = err\n      res.status(500).send(\"Something broke!\")\n    })\n  })\n\n  it(\"Should sent response only once\", async () => {\n    const response = await client\n      .get(\"/auth/session\")\n      .set(\"Accept\", \"application/json\")\n\n    expect(response.status).toBe(200)\n    expect(error).toBe(null)\n  })\n\n  it(\"Should send status 500 if there is an error thrown in the auth middleware\", async () => {\n    // send header that causes mock to throw\n    const response = await client\n      .get(\"/auth/session\")\n      .set(\"Accept\", \"application/json\")\n      .set(\"X-Test-Header\", \"throw\")\n\n    expect(response.status).toBe(500)\n  })\n})\n"
  },
  {
    "path": "packages/frameworks-express/test/session.test.ts",
    "content": "import { vi, describe, it, beforeEach, expect } from \"vitest\"\nimport supertest from \"supertest\"\nimport express from \"express\"\n\nconst sessionJson = {\n  user: {\n    name: \"John Doe\",\n    email: \"test@example.com\",\n    image: \"\",\n    id: \"1234\",\n  },\n  expires: \"\",\n}\n\nvi.mock(\"@auth/core\", async (importOriginal) => {\n  const mod = await importOriginal<typeof import(\"@auth/core\")>()\n  return {\n    ...mod,\n    Auth: vi.fn((request, config) => {\n      return new Response(JSON.stringify(sessionJson), {\n        status: 200,\n        headers: { \"Content-Type\": \"application/json\" },\n      })\n    }),\n  }\n})\n\n// dynamic import to avoid loading Auth before hoisting\nconst { getSession } = await import(\"../src/index.js\")\n\ndescribe(\"getSession\", () => {\n  let app: express.Express\n  let client: ReturnType<typeof supertest>\n\n  beforeEach(() => {\n    app = express()\n    client = supertest(app)\n  })\n\n  it(\"Should return the mocked session from the Auth response\", async () => {\n    let expectations: Function = () => {}\n\n    app.post(\"/\", async (req, res) => {\n      const session = await getSession(req, {\n        providers: [],\n        secret: \"secret\",\n      })\n\n      expectations = async () => {\n        expect(session).toEqual(sessionJson)\n      }\n\n      res.send(\"OK\")\n    })\n\n    await client\n      .post(\"/\")\n      .set(\"X-Test-Header\", \"foo\")\n      .set(\"Accept\", \"application/json\")\n\n    await expectations()\n  })\n})\n"
  },
  {
    "path": "packages/frameworks-express/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"*.js\", \"*.d.ts\", \"lib\"]\n}\n"
  },
  {
    "path": "packages/frameworks-express/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\", \"src/adapters.ts\", \"src/lib/index.ts\", \"src/lib/http-api-adapters.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/express\",\n  entryFileName: \"../express.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/frameworks-qwik/README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://qwik.dev\" target=\"_blank\"><img height=\"96px\" src=\"https://authjs.dev/img/etc/qwik.svg\" /></a>\n  <a href=\"https://qwik.authjs.dev\" target=\"_blank\"><img height=\"96px\" src=\"https://authjs.dev/img/logo-sm.png\" /></a>\n  <h1 align=\"center\">Qwik Auth</h1>\n</p>\n<p align=\"center\">\n  Authentication for Qwik.\n</p>\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@auth/qwik\"><img src=\"https://img.shields.io/npm/v/@auth/qwik?style=flat-square&label=latest&color=purple\" alt=\"npm latest release\" /></a>\n  <a href=\"https://www.npmtrends.com/@auth/qwik\"><img src=\"https://img.shields.io/npm/dm/@auth/qwik?style=flat-square&color=cyan\" alt=\"Downloads\" /></a>\n  <a href=\"https://github.com/nextauthjs/next-auth/stargazers\"><img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square&color=orange\" alt=\"GitHub Stars\" /></a>\n  <img src=\"https://shields.io/badge/TypeScript-3178C6?logo=TypeScript&logoColor=fff&style=flat-square\" alt=\"TypeScript\" />\n</p>\n\n---\n\nCheck out the documentation at [qwik.authjs.dev](https://qwik.authjs.dev).\n"
  },
  {
    "path": "packages/frameworks-qwik/package.json",
    "content": "{\n  \"name\": \"@auth/qwik\",\n  \"version\": \"0.9.0\",\n  \"description\": \"Authentication for Qwik.\",\n  \"license\": \"ISC\",\n  \"author\": \"gioboa <giorgiob.boa@gmail.com>\",\n  \"homepage\": \"https://qwik.authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"keywords\": [\n    \"Qwik\",\n    \"oauth\",\n    \"jwt\",\n    \"oauth2\",\n    \"authentication\",\n    \"csrf\",\n    \"oidc\",\n    \"Auth.js\"\n  ],\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"type\": \"module\",\n  \"engines\": {\n    \"node\": \">=18.17\"\n  },\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.qwik.js\"\n    },\n    \"./adapters\": {\n      \"types\": \"./adapters.d.ts\"\n    },\n    \"./providers\": {\n      \"types\": \"./providers/index.d.ts\"\n    },\n    \"./providers/*\": {\n      \"types\": \"./providers/*.d.ts\",\n      \"import\": \"./providers/*.js\"\n    }\n  },\n  \"qwik\": \"./index.qwik.js\",\n  \"files\": [\n    \"*.d.ts*\",\n    \"*.js\",\n    \"*.qwik.js\",\n    \"providers\",\n    \"src\"\n  ],\n  \"scripts\": {\n    \"build\": \"pnpm clean && pnpm providers && vite build --mode lib\",\n    \"clean\": \"rm -rf *.mjs *.js *.d.ts* providers src/providers\",\n    \"providers\": \"node ../utils/scripts/providers\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\",\n    \"set-cookie-parser\": \"^2.6.0\"\n  },\n  \"devDependencies\": {\n    \"@builder.io/qwik\": \"1.7.3\",\n    \"@builder.io/qwik-city\": \"1.5.5\",\n    \"@types/set-cookie-parser\": \"^2.4.7\",\n    \"typescript\": \"^5.4.5\",\n    \"vite-plugin-dts\": \"^3.9.1\",\n    \"vite-plugin-static-copy\": \"^1.0.5\"\n  }\n}\n"
  },
  {
    "path": "packages/frameworks-qwik/src/adapters.ts",
    "content": "export type * from \"@auth/core/adapters\"\n"
  },
  {
    "path": "packages/frameworks-qwik/src/index.ts",
    "content": "/**\n *\n * :::warning\n * `@auth/qwik` is currently experimental. The API _will_ change in the future.\n * :::\n *\n * Qwik Auth is the official Qwik integration for Auth.js.\n * It provides a simple way to add authentication to your Qwik app in a few lines of code.\n *\n * ## Installation\n * ```bash npm2yarn\n * npm install @auth/qwik\n * ```\n *\n * ## Usage\n *\n * ### Provider Configuration\n *\n * Create a `plugin@auth.ts` file in the routes folder\n *\n * import { QwikAuth$ } from \"@auth/qwik\"\n * import GitHub from \"@auth/qwik/providers/github\"\n *\n * export const {\n *   onRequest, useSession, useSignIn, useSignOut\n * } = QwikAuth$(() => ({ providers: [GitHub] }))\n *\n * ## Signing in and signing out\n *\n * Sign In via form\n *\n * import { component$ } from '@builder.io/qwik';\n * import { Form } from '@builder.io/qwik-city';\n * import { useSignIn } from '~/routes/plugin@auth';\n *\n * export default component$(() => {\n *   const signIn = useSignIn();\n *   return (\n *     <Form action={signIn}>\n *       <input type=\"hidden\" name=\"providerId\" value=\"github\" />\n *       <input type=\"hidden\" name=\"options.redirectTo\" value=\"http://qwik-auth-example.com/dashboard\" />\n *       <button>Sign In</button>\n *     </Form>\n *   );\n * });\n *\n * Sign In via code\n *\n * import { component$ } from '@builder.io/qwik';\n * import { useSignIn } from '~/routes/plugin@auth';\n *\n * export default component$(() => {\n *   const signIn = useSignIn();\n *   return (\n *     <button onClick$={() => signIn.submit({ providerId: 'github', options: { redirectTo: 'http://qwik-auth-example.com/dashboard' } })}>Sign In</button>\n *   );\n * });\n *\n * Sign out via form\n *\n * import { component$ } from '@builder.io/qwik';\n * import { Form } from '@builder.io/qwik-city';\n * import { useSignOut } from '~/routes/plugin@auth';\n *\n * export default component$(() => {\n *   const signOut = useSignOut();\n *   return (\n *     <Form action={signOut}>\n *       <input type=\"hidden\" name=\"redirectTo\" value=\"/signedout\" />\n *       <button>Sign Out</button>\n *     </Form>\n *   );\n * });\n *\n * Sign out via code\n *\n * import { component$ } from '@builder.io/qwik';\n * import { useSignOut } from '~/routes/plugin@auth';\n *\n * export default component$(() => {\n *   const signOut = useSignOut();\n *   return <button onClick$={() => signOut.submit({ redirectTo: '/signedout' })}>Sign Out</button>;\n * });\n *\n * ## Managing the session\n *\n * import { component$ } from '@builder.io/qwik';\n * import { useSession } from '~/routes/plugin@auth';\n *\n * export default component$(() => {\n *   const session = useSession();\n *   return <p>{session.value?.user?.email}</p>;\n * });\n *\n * ## Authorization\n *\n * Session data can be accessed via the route event.sharedMap.\n * So a route can be protected and redirect using something like this located in a layout.tsx or page index.tsx:\n *\n * export const onRequest: RequestHandler = (event) => {\n *   const session = event.sharedMap.get(\"session\")\n *   if (!session || new Date(session.expires) < new Date()) {\n *     throw event.redirect(302, `/auth/signin?redirectTo=${event.url.pathname}`);\n *   }\n * };\n *\n * ```\n *\n * @module @auth/qwik\n */\n\nimport type { AuthConfig } from \"@auth/core\"\nimport {\n  Auth,\n  isAuthAction,\n  skipCSRFCheck,\n  customFetch as _customFetch,\n} from \"@auth/core\"\nimport { AuthAction, Session } from \"@auth/core/types\"\nimport { implicit$FirstArg, type QRL } from \"@builder.io/qwik\"\nimport {\n  globalAction$,\n  routeLoader$,\n  z,\n  zod$,\n  type RequestEventCommon,\n} from \"@builder.io/qwik-city\"\nimport { EnvGetter } from \"@builder.io/qwik-city/middleware/request-handler\"\nimport { isServer } from \"@builder.io/qwik/build\"\nimport { parseString, splitCookiesString } from \"set-cookie-parser\"\n\nexport const customFetch = isServer ? _customFetch : undefined\n\nexport { AuthError, CredentialsSignin } from \"@auth/core/errors\"\n\nexport type {\n  Account,\n  DefaultSession,\n  Profile,\n  Session,\n  User,\n} from \"@auth/core/types\"\n\n/** Configure the {@link QwikAuth$} method. */\nexport interface QwikAuthConfig extends Omit<AuthConfig, \"raw\"> {}\n\nexport type GetSessionResult = Promise<{ data: Session | null; cookie: any }>\n\n/** @internal */\nexport function QwikAuthQrl(\n  authOptions: QRL<(ev: RequestEventCommon) => QwikAuthConfig>\n) {\n  const useSignIn = globalAction$(\n    async (\n      { providerId, redirectTo: _redirectTo, options, authorizationParams },\n      req\n    ) => {\n      if (!isServer) return\n      const { redirectTo = _redirectTo ?? defaultRedirectTo(req), ...rest } =\n        options ?? {}\n\n      const isCredentials = providerId === \"credentials\"\n\n      const authOpts = await authOptions(req)\n      setEnvDefaults(req.env, authOpts)\n      const body = new URLSearchParams({ callbackUrl: redirectTo })\n      Object.entries(rest).forEach(([key, value]) => {\n        body.set(key, String(value))\n      })\n\n      const baseSignInUrl = `/auth/${isCredentials ? \"callback\" : \"signin\"}${\n        providerId ? `/${providerId}` : \"\"\n      }`\n      const signInUrl = `${baseSignInUrl}?${new URLSearchParams(\n        authorizationParams\n      )}`\n\n      const data = await authAction(body, req, signInUrl, authOpts)\n\n      if (data.url) {\n        throw req.redirect(301, data.url)\n      }\n    },\n    zod$({\n      providerId: z.string().optional(),\n      redirectTo: z.string().optional(),\n      options: z\n        .object({\n          redirectTo: z.string(),\n        })\n        .passthrough()\n        .partial()\n        .optional(),\n      authorizationParams: z\n        .union([z.string(), z.custom<URLSearchParams>(), z.record(z.string())])\n        .optional(),\n    })\n  )\n\n  const useSignOut = globalAction$(\n    async ({ redirectTo }, req) => {\n      if (!isServer) return\n      redirectTo ??= defaultRedirectTo(req)\n      const authOpts = await authOptions(req)\n      setEnvDefaults(req.env, authOpts)\n      const body = new URLSearchParams({ callbackUrl: redirectTo })\n      await authAction(body, req, `/auth/signout`, authOpts)\n    },\n    zod$({\n      redirectTo: z.string().optional(),\n    })\n  )\n\n  const useSession = routeLoader$((req) => {\n    return req.sharedMap.get(\"session\") as Session | null\n  })\n\n  const onRequest = async (req: RequestEventCommon) => {\n    if (isServer) {\n      const prefix: string = \"/auth\"\n\n      const action = req.url.pathname\n        .slice(prefix.length + 1)\n        .split(\"/\")[0] as AuthAction\n\n      const authOpts = await authOptions(req)\n      setEnvDefaults(req.env, authOpts)\n      if (isAuthAction(action) && req.url.pathname.startsWith(prefix + \"/\")) {\n        const res = (await Auth(req.request, authOpts)) as Response\n        const cookie = res.headers.get(\"set-cookie\")\n        if (cookie) {\n          req.headers.set(\"set-cookie\", cookie)\n          res.headers.delete(\"set-cookie\")\n          fixCookies(req)\n        }\n        throw req.send(res)\n      } else {\n        const { data, cookie } = await getSessionData(req.request, authOpts)\n        req.sharedMap.set(\"session\", data)\n        if (cookie) {\n          req.headers.set(\"set-cookie\", cookie)\n          fixCookies(req)\n        }\n      }\n    }\n  }\n\n  return { useSignIn, useSignOut, useSession, onRequest }\n}\n\n/**\n *  Initialize Qwik Auth.\n *\n *  @example\n * ```ts title=\"plugin@auth.ts\"\n * import { QwikAuth } from \"@auth/qwik\"\n * import GitHub from \"@auth/qwik/providers/github\"\n *\n * export const {\n *   onRequest, useSession, useSignIn, useSignOut\n * } = QwikAuth$(() => ({ providers: [GitHub] }))\n * ```\n */\nexport const QwikAuth$ = /*#__PURE__*/ implicit$FirstArg(QwikAuthQrl)\n\nasync function authAction(\n  body: URLSearchParams | undefined,\n  req: RequestEventCommon,\n  path: string,\n  authOptions: QwikAuthConfig\n) {\n  if (!isServer) return\n  const request = new Request(new URL(path, req.request.url), {\n    method: req.request.method,\n    headers: req.request.headers,\n    body: body,\n  })\n  request.headers.set(\"content-type\", \"application/x-www-form-urlencoded\")\n  const res = (await Auth(request, authOptions)) as Response\n\n  const cookies: string[] = []\n  res.headers.forEach((value, key) => {\n    if (key === \"set-cookie\") {\n      // while browsers would support setting multiple cookies, the fetch implementation does not, so we join them later.\n      cookies.push(value)\n    } else if (!req.headers.has(key)) {\n      req.headers.set(key, value)\n    }\n  })\n\n  if (cookies.length > 0) {\n    req.headers.set(\"set-cookie\", cookies.join(\", \"))\n  }\n\n  fixCookies(req)\n\n  try {\n    return await res.json()\n  } catch {\n    return await res.text()\n  }\n}\n\nconst fixCookies = (req: RequestEventCommon) => {\n  req.headers.set(\"set-cookie\", req.headers.get(\"set-cookie\") || \"\")\n  const cookie = req.headers.get(\"set-cookie\")\n  if (cookie) {\n    req.headers.delete(\"set-cookie\")\n    splitCookiesString(cookie).forEach((cookie) => {\n      const { name, value, ...rest } = parseString(cookie)\n      req.cookie.set(name, value, rest as any)\n    })\n  }\n}\n\nconst defaultRedirectTo = (req: RequestEventCommon) => {\n  req.url.searchParams.delete(\"qaction\")\n  return req.url.href\n}\n\nasync function getSessionData(\n  req: Request,\n  options: AuthConfig\n): GetSessionResult {\n  options.secret ??= process.env.AUTH_SECRET\n  options.trustHost ??= true\n\n  const url = new URL(\"/auth/session\", req.url)\n  const response = (await Auth(\n    new Request(url, { headers: req.headers }),\n    options\n  )) as Response\n\n  const { status = 200 } = response\n\n  const data = await response.json()\n  const cookie = response.headers.get(\"set-cookie\")\n  if (!data || !Object.keys(data).length) {\n    return { data: null, cookie }\n  }\n  if (status === 200) {\n    return { data, cookie }\n  }\n\n  throw new Error(data.message)\n}\n\nexport const setEnvDefaults = (env: EnvGetter, config: AuthConfig) => {\n  if (!isServer) return\n  config.basePath = \"/auth\"\n  if (!config.secret?.length) {\n    config.secret = []\n    const secret = env.get(\"AUTH_SECRET\")\n    if (secret) {\n      config.secret.push(secret)\n    }\n    for (const i of [1, 2, 3]) {\n      const secret = env.get(`AUTH_SECRET_${i}`)\n      if (secret) {\n        config.secret.unshift(secret)\n      }\n    }\n  }\n\n  config.redirectProxyUrl ??= env.get(\"AUTH_REDIRECT_PROXY_URL\")\n  config.trustHost ??= !!(\n    env.get(\"AUTH_URL\") ??\n    env.get(\"AUTH_TRUST_HOST\") ??\n    env.get(\"VERCEL\") ??\n    env.get(\"CF_PAGES\") ??\n    env.get(\"NODE_ENV\") !== \"production\"\n  )\n  config.skipCSRFCheck = skipCSRFCheck\n\n  config.providers = config.providers.map((p) => {\n    const finalProvider = typeof p === \"function\" ? p({}) : p\n    const ID = finalProvider.id.toUpperCase().replace(/-/g, \"_\")\n    if (finalProvider.type === \"oauth\" || finalProvider.type === \"oidc\") {\n      finalProvider.clientId ??= env.get(`AUTH_${ID}_ID`)\n      finalProvider.clientSecret ??= env.get(`AUTH_${ID}_SECRET`)\n      if (finalProvider.type === \"oidc\") {\n        finalProvider.issuer ??= env.get(`AUTH_${ID}_ISSUER`)\n      }\n    } else if (finalProvider.type === \"email\") {\n      finalProvider.apiKey ??= env.get(`AUTH_${ID}_KEY`)\n    }\n    return finalProvider\n  })\n}\n"
  },
  {
    "path": "packages/frameworks-qwik/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"*.js\", \"*.d.ts\", \"lib\"]\n}\n"
  },
  {
    "path": "packages/frameworks-qwik/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/qwik\",\n  entryFileName: \"../qwik.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/frameworks-qwik/vite.config.ts",
    "content": "import { qwikVite } from \"@builder.io/qwik/optimizer\"\nimport { join } from \"path\"\nimport { defineConfig } from \"vite\"\nimport dts from \"vite-plugin-dts\"\nimport { viteStaticCopy } from \"vite-plugin-static-copy\"\n\nexport default defineConfig(() => {\n  return {\n    build: {\n      minify: false,\n      target: \"es2020\",\n      outDir: \".\",\n      lib: {\n        entry: [\"./src/index.ts\"],\n        formats: [\"es\"],\n        fileName: (_, entryName) => `${entryName}.qwik.js`,\n      },\n      rollupOptions: {\n        external: [\n          \"@builder.io/qwik\",\n          \"@builder.io/qwik-city\",\n          \"@builder.io/qwik/build\",\n          \"@auth/core\",\n        ],\n      },\n    },\n    plugins: [\n      qwikVite(),\n      dts({\n        tsconfigPath: join(__dirname, \"tsconfig.json\"),\n      }),\n      viteStaticCopy({\n        targets: [\n          {\n            src: \"src/providers/*\",\n            dest: \"providers/\",\n            rename: (fileName: string) => `${fileName}.js`,\n          },\n        ],\n      }),\n    ],\n  }\n})\n"
  },
  {
    "path": "packages/frameworks-solid-start/.gitignore",
    "content": "node_modules\ndist\n**/*.d.ts\n**/*.js\n!tsup.config.js\n!scripts/**/*.js\n.vercel\n"
  },
  {
    "path": "packages/frameworks-solid-start/README.MD",
    "content": "<p align=\"center\">\n  <a href=\"https://start.solidjs.com\" target=\"_blank\"><img height=\"96px\" src=\"https://authjs.dev/img/etc/solidstart.svg\" /></a>\n  <a href=\"https://solid-start.authjs.dev\" target=\"_blank\"><img height=\"96px\" src=\"https://authjs.dev/img/logo-sm.png\" /></a>\n  <h1 align=\"center\">SolidStart Auth</h1>\n</p>\n<p align=\"center\">\n  Authentication for SolidStart.\n</p>\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@auth/solid-start\"><img src=\"https://img.shields.io/npm/v/@auth/solid-start?style=flat-square&label=latest&color=purple\" alt=\"npm latest release\" /></a>\n  <a href=\"https://www.npmtrends.com/@auth/solid-start\"><img src=\"https://img.shields.io/npm/dm/@auth/solid-start?style=flat-square&color=cyan\" alt=\"Downloads\" /></a>\n  <a href=\"https://github.com/nextauthjs/next-auth/stargazers\"><img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square&color=orange\" alt=\"GitHub Stars\" /></a>\n  <img src=\"https://shields.io/badge/TypeScript-3178C6?logo=TypeScript&logoColor=fff&style=flat-square\" alt=\"TypeScript\" />\n</p>\n\n---\n\nCheck out the documentation at [solid-start.authjs.dev](https://solid-start.authjs.dev).\n"
  },
  {
    "path": "packages/frameworks-solid-start/package.json",
    "content": "{\n  \"name\": \"@auth/solid-start\",\n  \"version\": \"0.19.0\",\n  \"description\": \"Authentication for SolidStart.\",\n  \"license\": \"ISC\",\n  \"author\": \"OrJDev <orjdeveloper@gmail.com>\",\n  \"homepage\": \"https://solid-start.authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"keywords\": [\n    \"SolidJS\",\n    \"SolidStart\",\n    \"oauth\",\n    \"jwt\",\n    \"oauth2\",\n    \"authentication\",\n    \"nextjs\",\n    \"csrf\",\n    \"oidc\",\n    \"Auth.js\"\n  ],\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"type\": \"module\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"providers\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    },\n    \"./adapters\": {\n      \"types\": \"./adapters.d.ts\"\n    },\n    \"./client\": {\n      \"types\": \"./client.d.ts\",\n      \"import\": \"./client.js\"\n    },\n    \"./providers\": {\n      \"types\": \"./providers/index.d.ts\"\n    },\n    \"./providers/*\": {\n      \"types\": \"./providers/*.d.ts\",\n      \"import\": \"./providers/*.js\"\n    }\n  },\n  \"scripts\": {\n    \"build\": \"pnpm clean && pnpm providers && tsc\",\n    \"test\": \"vitest run -c ../utils/vitest.config.ts\",\n    \"providers\": \"node ../utils/scripts/providers\",\n    \"clean\": \"rm -rf *.js *.d.ts* providers src/providers\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"@solidjs/meta\": \"^0.28.0\",\n    \"@types/node\": \"^18.7.14\",\n    \"solid-js\": \"^1.5.7\",\n    \"solid-start\": \"^0.2.14\"\n  },\n  \"peerDependencies\": {\n    \"solid-js\": \"^1.5.7\",\n    \"solid-start\": \"^0.2.14\"\n  }\n}\n"
  },
  {
    "path": "packages/frameworks-solid-start/src/adapters.ts",
    "content": "export type * from \"@auth/core/adapters\"\n"
  },
  {
    "path": "packages/frameworks-solid-start/src/client.ts",
    "content": "import type { ProviderId } from \"@auth/core/providers\"\n\ninterface SignInOptions<Redirect extends boolean = true>\n  extends Record<string, unknown> {\n  /** @deprecated Use `redirectTo` instead. */\n  callbackUrl?: string\n  /**\n   * Specify where the user should be redirected to after a successful signin.\n   *\n   * By default, it is the page the sign-in was initiated from.\n   */\n  redirectTo?: string\n  /**\n   * You might want to deal with the signin response on the same page, instead of redirecting to another page.\n   * For example, if an error occurs (like wrong credentials given by the user), you might want to show an inline error message on the input field.\n   *\n   * For this purpose, you can set this to option `redirect: false`.\n   */\n  redirect?: Redirect\n}\n\nexport interface SignInResponse {\n  error: string | undefined\n  code: string | undefined\n  status: number\n  ok: boolean\n  url: string | null\n}\n\ninterface SignOutParams<R extends boolean = true> {\n  /** [Documentation](https://next-auth.js.org/getting-started/client#specifying-a-callbackurl-1) */\n  callbackUrl?: string\n  /** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1 */\n  redirect?: R\n}\n\n/** Match `inputType` of `new URLSearchParams(inputType)` */\nexport type SignInAuthorizationParams =\n  | string\n  | string[][]\n  | Record<string, string>\n  | URLSearchParams\n\n/**\n * Client-side method to initiate a signin flow\n * or send the user to the signin page listing all possible providers.\n * Automatically adds the CSRF token to the request.\n *\n * ```ts\n * import { signIn } from \"@auth/solid-start/client\"\n * signIn()\n * signIn(\"provider\") // example: signIn(\"github\")\n * ```\n */\n\n/**\n * Initiates a signin flow or sends the user to the signin page listing all possible providers.\n * Handles CSRF protection.\n *\n * @note This method can only be used from Client Components (\"use client\" or Pages Router).\n * For Server Actions, use the `signIn` method imported from the `auth` config.\n */\nexport async function signIn(\n  provider?: ProviderId,\n  options?: SignInOptions<true>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<void>\nexport async function signIn(\n  provider?: ProviderId,\n  options?: SignInOptions<false>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<SignInResponse>\nexport async function signIn<Redirect extends boolean = true>(\n  provider?: ProviderId,\n  options?: SignInOptions<Redirect>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<SignInResponse | void> {\n  const { callbackUrl, ...rest } = options ?? {}\n  const {\n    redirect = true,\n    redirectTo = callbackUrl ?? window.location.href,\n    ...signInParams\n  } = rest\n\n  const isCredentials = provider === \"credentials\"\n\n  const signInUrl = `/api/auth/${\n    isCredentials ? \"callback\" : \"signin\"\n  }/${provider}`\n\n  // TODO: Handle custom base path\n  const csrfTokenResponse = await fetch(\"/api/auth/csrf\")\n  const { csrfToken } = await csrfTokenResponse.json()\n  const res = await fetch(\n    `${signInUrl}?${new URLSearchParams(authorizationParams)}`,\n    {\n      method: \"post\",\n      headers: {\n        \"Content-Type\": \"application/x-www-form-urlencoded\",\n        \"X-Auth-Return-Redirect\": \"1\",\n      },\n      body: new URLSearchParams({\n        ...signInParams,\n        csrfToken,\n        callbackUrl: redirectTo,\n      }),\n    }\n  )\n\n  const data = await res.json()\n\n  if (redirect) {\n    const url = data.url ?? redirectTo\n    window.location.href = url\n    // If url contains a hash, the browser does not reload the page. We reload manually\n    if (url.includes(\"#\")) window.location.reload()\n    return\n  }\n\n  const error = new URL(data.url).searchParams.get(\"error\") ?? undefined\n  const code = new URL(data.url).searchParams.get(\"code\") ?? undefined\n\n  return {\n    error,\n    code,\n    status: res.status,\n    ok: res.ok,\n    url: error ? null : data.url,\n  }\n}\n\n/**\n * Signs the user out, by removing the session cookie.\n * Automatically adds the CSRF token to the request.\n *\n * ```ts\n * import { signOut } from \"@auth/solid-start/client\"\n * signOut()\n * ```\n */\nexport async function signOut(options?: SignOutParams) {\n  const { callbackUrl = window.location.href } = options ?? {}\n  // TODO: Custom base path\n  const csrfTokenResponse = await fetch(\"/api/auth/csrf\")\n  const { csrfToken } = await csrfTokenResponse.json()\n  const res = await fetch(`/api/auth/signout`, {\n    method: \"post\",\n    headers: {\n      \"Content-Type\": \"application/x-www-form-urlencoded\",\n      \"X-Auth-Return-Redirect\": \"1\",\n    },\n    body: new URLSearchParams({\n      csrfToken,\n      callbackUrl,\n    }),\n  })\n  const data = await res.json()\n\n  const url = data.url ?? data.redirect ?? callbackUrl\n  window.location.href = url\n  // If url contains a hash, the browser does not reload the page. We reload manually\n  if (url.includes(\"#\")) window.location.reload()\n}\n"
  },
  {
    "path": "packages/frameworks-solid-start/src/index.ts",
    "content": "/**\n *\n * :::warning\n * `@auth/solid-start` is currently experimental. The API _will_ change in the future.\n * :::\n *\n * SolidStart Auth is the official SolidStart integration for Auth.js.\n * It provides a simple way to add authentication to your SolidStart app in a few lines of code.\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/core @auth/solid-start\n * ```\n *\n * @module @auth/solid-start\n */\n\nimport { Auth, type AuthConfig, customFetch } from \"@auth/core\"\nimport type { AuthAction, Session } from \"@auth/core/types\"\n\nexport { customFetch }\nexport { AuthError, CredentialsSignin } from \"@auth/core/errors\"\nexport type {\n  Account,\n  DefaultSession,\n  Profile,\n  Session,\n  User,\n} from \"@auth/core/types\"\n\nexport interface SolidAuthConfig extends AuthConfig {\n  /**\n   * Defines the base path for the auth routes.\n   * @default '/api/auth'\n   */\n  prefix?: string\n}\n\nconst actions: AuthAction[] = [\n  \"providers\",\n  \"session\",\n  \"csrf\",\n  \"signin\",\n  \"signout\",\n  \"callback\",\n  \"verify-request\",\n  \"error\",\n]\n\nfunction SolidAuthHandler(prefix: string, authOptions: SolidAuthConfig) {\n  return async (event: any) => {\n    const { request } = event\n    const url = new URL(request.url)\n    const action = url.pathname\n      .slice(prefix.length + 1)\n      .split(\"/\")[0] as AuthAction\n\n    if (!actions.includes(action) || !url.pathname.startsWith(prefix + \"/\")) {\n      return\n    }\n\n    return await Auth(request, authOptions)\n  }\n}\n\n/**\n * ## Setup\n *\n * [Generate an auth secret](https://generate-secret.vercel.app/32), then set it as an environment variable:\n *\n * ```\n * AUTH_SECRET=your_auth_secret\n * ```\n *\n * ## Creating the API handler\n *\n * This example uses github, make sure to set the following environment variables:\n *\n * ```\n * GITHUB_ID=your_github_oauth_id\n * GITHUB_SECRET=your_github_oauth_secret\n * ```\n *\n * ```ts\n * // routes/api/auth/[...solidauth].ts\n * import { SolidAuth, type SolidAuthConfig } from \"@auth/solid-start\"\n * import GitHub from \"@auth/core/providers/github\"\n *\n * export const authOpts: SolidAuthConfig = {\n *   providers: [\n *     GitHub({\n *       clientId: process.env.GITHUB_ID,\n *       clientSecret: process.env.GITHUB_SECRET,\n *     }),\n *   ],\n *   debug: false,\n * }\n *\n * export const { GET, POST } = SolidAuth(authOpts)\n * ```\n *\n * ## Getting the current session\n *\n * ```ts\n * import { getSession } from \"@auth/solid-start\"\n * import { createServerData$ } from \"solid-start/server\"\n * import { authOpts } from \"~/routes/api/auth/[...solidauth]\"\n *\n * export const useSession = () => {\n *   return createServerData$(\n *     async (_, { request }) => {\n *       return await getSession(request, authOpts)\n *     },\n *     { key: () => [\"auth_user\"] }\n *   )\n * }\n *\n * // useSession returns a resource:\n * const session = useSession()\n * const loading = session.loading\n * const user = () => session()?.user\n * ```\n * ## Protected Routes\n *\n * ### When Using SSR\n *\n * When using SSR, it is recommended to create a `Protected` component that will trigger suspense using the `Show` component. It should look like this:\n *\n *\n * ```tsx\n * // components/Protected.tsx\n * import { type Session } from \"@auth/core/types\";\n * import { getSession } from \"@auth/solid-start\";\n * import { Component, Show } from \"solid-js\";\n * import { useRouteData } from \"solid-start\";\n * import { createServerData$, redirect } from \"solid-start/server\";\n * import { authOpts } from \"~/routes/api/auth/[...solidauth]\";\n *\n * const Protected = (Comp: IProtectedComponent) => {\n *   const routeData = () => {\n *     return createServerData$(\n *       async (_, event) => {\n *         const session = await getSession(event.request, authOpts);\n *         if (!session || !session.user) {\n *           throw redirect(\"/\");\n *         }\n *         return session;\n *       },\n *       { key: () => [\"auth_user\"] }\n *     );\n *   };\n *\n *   return {\n *     routeData,\n *     Page: () => {\n *       const session = useRouteData<typeof routeData>();\n *       return (\n *         <Show when={session()} keyed>\n *           {(sess) => <Comp {...sess} />}\n *         </Show>\n *       );\n *     },\n *   };\n * };\n *\n * type IProtectedComponent = Component<Session>;\n *\n * export default Protected;\n * ```\n *\n * It can be used like this:\n *\n *\n * ```tsx\n * // routes/protected.tsx\n * import Protected from \"~/components/Protected\";\n *\n * export const { routeData, Page } = Protected((session) => {\n *   return (\n *     <main class=\"flex flex-col gap-2 items-center\">\n *       <h1>This is a protected route</h1>\n *     </main>\n *   );\n * });\n *\n * export default Page;\n * ```\n *\n * ### When Using CSR\n *\n * When using CSR, the `Protected` component will not work as expected and will cause the screen to flash. To fix this, a Solid-Start middleware is used:\n *\n * ```tsx\n * // entry-server.tsx\n * import { Session } from \"@auth/core\";\n * import { getSession } from \"@auth/solid-start\";\n * import { redirect } from \"solid-start\";\n * import {\n *   StartServer,\n *   createHandler,\n *   renderAsync,\n * } from \"solid-start/entry-server\";\n * import { authOpts } from \"./routes/api/auth/[...solidauth]\";\n *\n * const protectedPaths = [\"/protected\"]; // add any route you wish in here\n *\n * export default createHandler(\n *   ({ forward }) => {\n *     return async (event) => {\n *       if (protectedPaths.includes(new URL(event.request.url).pathname)) {\n *         const session = await getSession(event.request, authOpts);\n *         if (!session) {\n *           return redirect(\"/\");\n *         }\n *       }\n *       return forward(event);\n *     };\n *   },\n *   renderAsync((event) => <StartServer event={event} />)\n * );\n * ```\n *\n * And now a protected route can be created:\n *\n *\n * ```tsx\n * // routes/protected.tsx\n * export default () => {\n *   return (\n *     <main class=\"flex flex-col gap-2 items-center\">\n *       <h1>This is a protected route</h1>\n *     </main>\n *   );\n * };\n * ```\n *\n * :::note\n * The CSR method should also work when using SSR, the SSR method shouldn't work when using CSR\n * :::\n *\n */\nexport function SolidAuth(config: SolidAuthConfig) {\n  const { prefix = \"/api/auth\", ...authOptions } = config\n  authOptions.secret ??= process.env.AUTH_SECRET\n  authOptions.trustHost ??= !!(\n    process.env.AUTH_TRUST_HOST ??\n    process.env.VERCEL ??\n    process.env.NODE_ENV !== \"production\"\n  )\n  const handler = SolidAuthHandler(prefix, authOptions)\n  return {\n    async GET(event: any) {\n      return await handler(event)\n    },\n    async POST(event: any) {\n      return await handler(event)\n    },\n  }\n}\n\nexport type GetSessionResult = Promise<Session | null>\n\nexport async function getSession(\n  req: Request,\n  options: Omit<AuthConfig, \"raw\">\n): GetSessionResult {\n  options.secret ??= process.env.AUTH_SECRET\n  options.trustHost ??= true\n\n  const url = new URL(\"/api/auth/session\", req.url)\n  const response = await Auth(\n    new Request(url, { headers: req.headers }),\n    options\n  )\n\n  const { status = 200 } = response\n\n  const data = await response.json()\n\n  if (!data || !Object.keys(data).length) return null\n  if (status === 200) return data\n  throw new Error(data.message)\n}\n"
  },
  {
    "path": "packages/frameworks-solid-start/test/index.test.ts",
    "content": "import { describe, test, expect } from \"vitest\"\n\ndescribe(\"@auth/solid-start\", () => {\n  test(\"should work\", () => {\n    expect(true).toBe(true)\n  })\n})\n"
  },
  {
    "path": "packages/frameworks-solid-start/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"jsx\": \"preserve\",\n    \"jsxImportSource\": \"solid-js\",\n    \"outDir\": \".\",\n    \"rootDir\": \"./src\"\n  },\n  \"include\": [\"./src/**/*\"],\n  \"exclude\": [\"*.js\", \"*.d.ts\"]\n}\n"
  },
  {
    "path": "packages/frameworks-solid-start/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\", \"src/client.ts\", \"src/adapters.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/solid-start\",\n  entryFileName: \"../solid-start.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/frameworks-sveltekit/README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://kit.svelte.dev\" target=\"_blank\"><img height=\"96px\" src=\"https://authjs.dev/img/etc/sveltekit.svg\" /></a>\n  <a href=\"https://sveltekit.authjs.dev\" target=\"_blank\"><img height=\"96px\" src=\"https://authjs.dev/img/logo-sm.png\" /></a>\n  <h1 align=\"center\">SvelteKit Auth</h1>\n</p>\n<p align=\"center\">\n  Authentication for SvelteKit.\n</p>\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@auth/sveltekit\"><img src=\"https://img.shields.io/npm/v/@auth/sveltekit?style=flat-square&label=latest&color=purple\" alt=\"npm latest release\" /></a>\n  <a href=\"https://www.npmtrends.com/@auth/sveltekit\"><img src=\"https://img.shields.io/npm/dm/@auth/sveltekit?style=flat-square&color=cyan\" alt=\"Downloads\" /></a>\n  <a href=\"https://github.com/nextauthjs/next-auth/stargazers\"><img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square&color=orange\" alt=\"GitHub Stars\" /></a>\n  <img src=\"https://shields.io/badge/TypeScript-3178C6?logo=TypeScript&logoColor=fff&style=flat-square\" alt=\"TypeScript\" />\n</p>\n\n---\n\nCheck out the documentation at [sveltekit.authjs.dev](https://sveltekit.authjs.dev).\n"
  },
  {
    "path": "packages/frameworks-sveltekit/package.json",
    "content": "{\n  \"name\": \"@auth/sveltekit\",\n  \"version\": \"1.11.0\",\n  \"description\": \"Authentication for SvelteKit.\",\n  \"keywords\": [\n    \"authentication\",\n    \"authjs\",\n    \"jwt\",\n    \"sveltekit\",\n    \"oauth\",\n    \"oidc\",\n    \"passwordless\",\n    \"svelte\"\n  ],\n  \"license\": \"ISC\",\n  \"homepage\": \"https://sveltekit.authjs.dev\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/nextauthjs/next-auth\",\n    \"directory\": \"packages/frameworks-sveltekit\"\n  },\n  \"author\": \"Thang Huu Vu <hi@thvu.dev>\",\n  \"contributors\": [\n    \"Thang Huu Vu <hi@thvu.dev>\",\n    \"Balázs Orbán <info@balazsorban.com>\",\n    \"Nico Domino <yo@ndo.dev>\",\n    \"Lluis Agusti <hi@llu.lu>\",\n    \"Iain Collins <me@iaincollins.com>\"\n  ],\n  \"scripts\": {\n    \"build\": \"pnpm clean && pnpm providers && pnpm check && svelte-package\",\n    \"check:watch\": \"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch\",\n    \"check\": \"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json\",\n    \"clean\": \"rm -rf dist src/lib/providers\",\n    \"dev\": \"pnpm providers && svelte-package -w\",\n    \"preview\": \"vite preview\",\n    \"providers\": \"node ../utils/scripts/providers.js --out src/lib\",\n    \"test\": \"vitest run -c ../utils/vitest.config.ts\"\n  },\n  \"devDependencies\": {\n    \"@sveltejs/adapter-auto\": \"^3.2.5\",\n    \"@sveltejs/kit\": \"^2.6.4\",\n    \"@sveltejs/package\": \"^2.3.5\",\n    \"@sveltejs/vite-plugin-svelte\": \"^3.1.2\",\n    \"@types/set-cookie-parser\": \"^2.4.10\",\n    \"svelte\": \"^4.2.19\",\n    \"svelte-check\": \"^4.0.4\",\n    \"tslib\": \"^2.7.0\",\n    \"typedoc\": \"^0.25.12\",\n    \"typedoc-plugin-markdown\": \"4.0.0-next.53\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\",\n    \"set-cookie-parser\": \"^2.7.0\"\n  },\n  \"peerDependencies\": {\n    \"@simplewebauthn/browser\": \"^9.0.1\",\n    \"@simplewebauthn/server\": \"^9.0.3\",\n    \"@sveltejs/kit\": \"^1.0.0 || ^2.0.0\",\n    \"nodemailer\": \"^7.0.7\",\n    \"svelte\": \"^3.54.0 || ^4.0.0 || ^5.0.0-0\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@simplewebauthn/browser\": {\n      \"optional\": true\n    },\n    \"@simplewebauthn/server\": {\n      \"optional\": true\n    },\n    \"nodemailer\": {\n      \"optional\": true\n    }\n  },\n  \"type\": \"module\",\n  \"types\": \"./dist/index.d.ts\",\n  \"files\": [\n    \"dist\",\n    \"src\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"import\": \"./dist/index.js\"\n    },\n    \"./client\": {\n      \"types\": \"./dist/client.d.ts\",\n      \"import\": \"./dist/client.js\"\n    },\n    \"./webauthn\": {\n      \"types\": \"./dist/webauthn.d.ts\",\n      \"import\": \"./dist/webauthn.js\"\n    },\n    \"./components\": {\n      \"types\": \"./dist/components/index.d.ts\",\n      \"svelte\": \"./dist/components/index.js\"\n    },\n    \"./adapters\": {\n      \"types\": \"./dist/adapters.d.ts\"\n    },\n    \"./providers\": {\n      \"types\": \"./dist/providers/index.d.ts\"\n    },\n    \"./providers/*\": {\n      \"types\": \"./dist/providers/*.d.ts\",\n      \"import\": \"./dist/providers/*.js\"\n    },\n    \"./package.json\": \"./package.json\"\n  }\n}\n"
  },
  {
    "path": "packages/frameworks-sveltekit/playwright.config.ts",
    "content": "import type { PlaywrightTestConfig } from \"@playwright/test\"\n\nconst config: PlaywrightTestConfig = {\n  webServer: {\n    command: \"npm run build && npm run preview\",\n    port: 4173,\n  },\n  testDir: \"test\",\n}\n\nexport default config\n"
  },
  {
    "path": "packages/frameworks-sveltekit/scripts/postbuild.js",
    "content": "// After build, copy the files in ./package to the root directory, excluding the package.json file.\n\nimport fs from \"fs/promises\"\nimport path from \"path\"\n\nlet __dirname = path.dirname(new URL(import.meta.url).pathname)\n\n// The above hack to polyfill \"__dirname\" for ESM does not work on Windows computers,\n// so we might have to manually perform more steps.\n__dirname = __dirname.split(path.sep).join(path.posix.sep)\nif (__dirname.match(/^\\/\\w:\\//)) {\n  __dirname = __dirname.slice(3) // Remove the drive prefix.\n}\n\nconst root = path.join(__dirname, \"..\")\nconst pkgDir = path.join(root, \"package\")\n\nawait fs.cp(pkgDir, root, {\n  recursive: true,\n  filter: (src) => !src.includes(\"package.json\"),\n})\n"
  },
  {
    "path": "packages/frameworks-sveltekit/src/lib/actions.ts",
    "content": "import { redirect } from \"@sveltejs/kit\"\nimport type { RequestEvent } from \"@sveltejs/kit\"\nimport { parse } from \"set-cookie-parser\"\nimport { env } from \"$env/dynamic/private\"\n\nimport { Auth, createActionURL, raw } from \"@auth/core\"\nimport type { ProviderType } from \"@auth/core/providers\"\nimport type { SvelteKitAuthConfig } from \"./types\"\nimport { setEnvDefaults } from \"./env\"\n\ntype SignInParams = Parameters<App.Locals[\"signIn\"]>\nexport async function signIn(\n  provider: SignInParams[0],\n  options: SignInParams[1] = {},\n  authorizationParams: SignInParams[2],\n  config: SvelteKitAuthConfig,\n  event: RequestEvent\n) {\n  const {\n    request,\n    url: { protocol },\n  } = event\n  const headers = new Headers(request.headers)\n  const {\n    redirect: shouldRedirect = true,\n    redirectTo,\n    ...rest\n  } = options instanceof FormData ? Object.fromEntries(options) : options\n\n  const callbackUrl = redirectTo?.toString() ?? headers.get(\"Referer\") ?? \"/\"\n  const signInURL = createActionURL(\"signin\", protocol, headers, env, config)\n\n  if (!provider) {\n    signInURL.searchParams.append(\"callbackUrl\", callbackUrl)\n    if (shouldRedirect) redirect(302, signInURL.toString())\n    return signInURL.toString()\n  }\n\n  let url = `${signInURL}/${provider}?${new URLSearchParams(authorizationParams)}`\n  let foundProvider: { id?: SignInParams[0]; type?: ProviderType } = {}\n\n  for (const providerConfig of config.providers) {\n    const { options, ...defaults } =\n      typeof providerConfig === \"function\" ? providerConfig() : providerConfig\n    const id = (options?.id as string | undefined) ?? defaults.id\n    if (id === provider) {\n      foundProvider = {\n        id,\n        type: (options?.type as ProviderType | undefined) ?? defaults.type,\n      }\n      break\n    }\n  }\n\n  if (!foundProvider.id) {\n    const url = `${signInURL}?${new URLSearchParams({ callbackUrl })}`\n    if (shouldRedirect) redirect(302, url)\n    return url\n  }\n\n  if (foundProvider.type === \"credentials\") {\n    url = url.replace(\"signin\", \"callback\")\n  }\n\n  headers.set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n  const body = new URLSearchParams({ ...rest, callbackUrl })\n  const req = new Request(url, { method: \"POST\", headers, body })\n  const res = await Auth(req, { ...config, raw })\n\n  for (const c of res?.cookies ?? []) {\n    event.cookies.set(c.name, c.value, { path: \"/\", ...c.options })\n  }\n\n  if (shouldRedirect) {\n    return redirect(302, res.redirect!)\n  }\n\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  return res.redirect as any\n}\n\ntype SignOutParams = Parameters<App.Locals[\"signOut\"]>\nexport async function signOut(\n  options: SignOutParams[0],\n  config: SvelteKitAuthConfig,\n  event: RequestEvent\n) {\n  const {\n    request,\n    url: { protocol },\n  } = event\n  const headers = new Headers(request.headers)\n  headers.set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\n  const url = createActionURL(\"signout\", protocol, headers, env, config)\n  const callbackUrl = options?.redirectTo ?? headers.get(\"Referer\") ?? \"/\"\n  const body = new URLSearchParams({ callbackUrl })\n  const req = new Request(url, { method: \"POST\", headers, body })\n\n  const res = await Auth(req, { ...config, raw })\n\n  for (const c of res?.cookies ?? [])\n    event.cookies.set(c.name, c.value, { path: \"/\", ...c.options })\n\n  if (options?.redirect ?? true) return redirect(302, res.redirect!)\n\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  return res as any\n}\n\nexport async function auth(\n  event: RequestEvent,\n  config: SvelteKitAuthConfig\n): ReturnType<App.Locals[\"auth\"]> {\n  setEnvDefaults(env, config)\n  config.trustHost ??= true\n\n  const {\n    request: req,\n    url: { protocol },\n  } = event\n\n  const sessionUrl = createActionURL(\n    \"session\",\n    protocol,\n    req.headers,\n    env,\n    config\n  )\n  const request = new Request(sessionUrl, {\n    headers: { cookie: req.headers.get(\"cookie\") ?? \"\" },\n  })\n  const response = await Auth(request, config)\n\n  const authCookies = parse(response.headers.getSetCookie())\n  for (const cookie of authCookies) {\n    const { name, value, ...options } = cookie\n    // @ts-expect-error - Review: SvelteKit and set-cookie-parser are mismatching\n    event.cookies.set(name, value, { path: \"/\", ...options })\n  }\n\n  const { status = 200 } = response\n  const data = await response.json()\n\n  if (!data || !Object.keys(data).length) return null\n  if (status === 200) return data\n  throw new Error(data.message)\n}\n"
  },
  {
    "path": "packages/frameworks-sveltekit/src/lib/adapters.ts",
    "content": "export type * from \"@auth/core/adapters\"\n"
  },
  {
    "path": "packages/frameworks-sveltekit/src/lib/client.ts",
    "content": "import { base } from \"$app/paths\"\nimport type { ProviderId } from \"@auth/core/providers\"\n\nexport interface SignInOptions<Redirect extends boolean = true>\n  extends Record<string, unknown> {\n  /** @deprecated Use `redirectTo` instead. */\n  callbackUrl?: string\n  /**\n   * Specify where the user should be redirected to after a successful signin.\n   *\n   * By default, it is the page the sign-in was initiated from.\n   */\n  redirectTo?: string\n  /**\n   * You might want to deal with the signin response on the same page, instead of redirecting to another page.\n   * For example, if an error occurs (like wrong credentials given by the user), you might want to show an inline error message on the input field.\n   *\n   * For this purpose, you can set this to option `redirect: false`.\n   */\n  redirect?: Redirect\n}\n\nexport interface SignInResponse {\n  error: string | undefined\n  code: string | undefined\n  status: number\n  ok: boolean\n  url: string | null\n}\n\nexport interface SignOutParams<Redirect extends boolean = true> {\n  /** @deprecated Use `redirectTo` instead. */\n  callbackUrl?: string\n  /**\n   * If you pass `redirect: false`, the page will not reload.\n   * The session will be deleted, and `useSession` is notified, so any indication about the user will be shown as logged out automatically.\n   * It can give a very nice experience for the user.\n   */\n  redirectTo?: string\n  /** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1 */\n  redirect?: Redirect\n}\n\n/** Match `inputType` of `new URLSearchParams(inputType)` */\nexport type SignInAuthorizationParams =\n  | string\n  | string[][]\n  | Record<string, string>\n  | URLSearchParams\n\n/**\n * Client-side method to initiate a signin flow\n * or send the user to the signin page listing all possible providers.\n *\n * [Documentation](https://authjs.dev/reference/sveltekit/client#signin)\n */\n\n/**\n * Initiates a signin flow or sends the user to the signin page listing all possible providers.\n * Handles CSRF protection.\n *\n * @note This method can only be used from Client Components (\"use client\" or Pages Router).\n * For Server Actions, use the `signIn` method imported from the `auth` config.\n */\nexport async function signIn(\n  provider?: ProviderId,\n  options?: SignInOptions<true>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<void>\nexport async function signIn(\n  provider?: ProviderId,\n  options?: SignInOptions<false>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<SignInResponse>\nexport async function signIn<Redirect extends boolean = true>(\n  provider?: ProviderId,\n  options?: SignInOptions<Redirect>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<SignInResponse | void> {\n  const { callbackUrl, ...rest } = options ?? {}\n  const {\n    redirect = true,\n    redirectTo = callbackUrl ?? window.location.href,\n    ...signInParams\n  } = rest\n\n  const baseUrl = base ?? \"\"\n\n  const signInUrl = `${baseUrl}/auth/${\n    provider === \"credentials\" ? \"callback\" : \"signin\"\n  }/${provider}`\n\n  const res = await fetch(\n    `${signInUrl}?${new URLSearchParams(authorizationParams)}`,\n    {\n      method: \"post\",\n      headers: {\n        \"Content-Type\": \"application/x-www-form-urlencoded\",\n        \"X-Auth-Return-Redirect\": \"1\",\n      },\n      body: new URLSearchParams({\n        ...signInParams,\n        callbackUrl: redirectTo,\n      }),\n    }\n  )\n\n  const data = await res.json()\n\n  if (redirect) {\n    const url = data.url ?? redirectTo\n    window.location.href = url\n    // If url contains a hash, the browser does not reload the page. We reload manually\n    if (url.includes(\"#\")) window.location.reload()\n    return\n  }\n\n  const error = new URL(data.url).searchParams.get(\"error\") ?? undefined\n  const code = new URL(data.url).searchParams.get(\"code\") ?? undefined\n\n  return {\n    error,\n    code,\n    status: res.status,\n    ok: res.ok,\n    url: error ? null : data.url,\n  }\n}\n\nexport interface SignOutResponse {\n  url: string\n}\n\n/**\n * Initiate a signout, by destroying the current session.\n * Handles CSRF protection.\n *\n * @note This method can only be used from Client Components (\"use client\" or Pages Router).\n * For Server Actions, use the `signOut` method imported from the `auth` config.\n */\nexport async function signOut(options?: SignOutParams<true>): Promise<void>\nexport async function signOut(\n  options?: SignOutParams<false>\n): Promise<SignOutResponse>\nexport async function signOut<R extends boolean = true>(\n  options?: SignOutParams<R>\n): Promise<SignOutResponse | void> {\n  const {\n    redirect = true,\n    redirectTo = options?.callbackUrl ?? window.location.href,\n  } = options ?? {}\n\n  const baseUrl = base ?? \"\"\n  const res = await fetch(`${baseUrl}/auth/signout`, {\n    method: \"post\",\n    headers: {\n      \"Content-Type\": \"application/x-www-form-urlencoded\",\n      \"X-Auth-Return-Redirect\": \"1\",\n    },\n    body: new URLSearchParams({\n      callbackUrl: redirectTo,\n    }),\n  })\n  const data = await res.json()\n\n  if (redirect) {\n    const url = data.url ?? redirectTo\n    window.location.href = url\n    // If url contains a hash, the browser does not reload the page. We reload manually\n    if (url.includes(\"#\")) window.location.reload()\n    return\n  }\n\n  return data\n}\n"
  },
  {
    "path": "packages/frameworks-sveltekit/src/lib/components/SignIn.svelte",
    "content": "<script lang=\"ts\">\n  import { enhance } from \"$app/forms\"\n  import type { signIn } from \"$lib/actions\"\n\n  export let className = \"\"\n  // TODO: Lookup provider type by id\n  export let provider: Partial<Parameters<typeof signIn>[0]> = \"\"\n  /** The path to the FormAction file in your route. @default signin */\n  export let signInPage = \"signin\"\n  export let options: Parameters<typeof signIn>[1] | undefined = undefined\n  export let authorizationParams: Parameters<typeof signIn>[2] | undefined =\n    undefined\n  const callbackUrl =\n    options instanceof FormData\n      ? options.get(\"redirectTo\")\n      : options?.redirectTo\n  const redirect =\n    options instanceof FormData ? options.get(\"redirect\") : options?.redirectTo\n  const redirectTo = callbackUrl\n\n  const authorizationParamsInputs = authorizationParams\n    ? typeof authorizationParams === \"string\" && authorizationParams\n      ? new URLSearchParams(authorizationParams)\n      : authorizationParams\n    : undefined\n</script>\n\n<form\n  method=\"POST\"\n  action={`/${signInPage}`}\n  use:enhance\n  class={`signInButton ${className}`}\n  {...$$restProps}\n>\n  <input type=\"hidden\" name=\"providerId\" value={provider} />\n  {#if callbackUrl}\n    <input type=\"hidden\" name=\"callbackUrl\" value={callbackUrl} />\n  {/if}\n  {#if redirect}\n    <input type=\"hidden\" name=\"redirect\" value={redirect} />\n  {/if}\n  {#if redirectTo}\n    <input type=\"hidden\" name=\"redirectTo\" value={redirectTo} />\n  {/if}\n  {#if authorizationParamsInputs}\n    {#each Object.entries(authorizationParamsInputs) as [key, value]}\n      <input type=\"hidden\" name={`authorizationParams-${key}`} {value} />\n    {/each}\n  {/if}\n  {#if provider === \"credentials\"}\n    <slot name=\"credentials\" />\n  {/if}\n  <!-- TODO: Filter by provider type only -->\n  {#if provider === \"email\" || provider === \"sendgrid\" || provider === \"resend\"}\n    <slot name=\"email\">\n      <label\n        class=\"section-header\"\n        for={`input-email-for-${provider}-provider`}\n      >\n        Email\n      </label>\n      <input\n        id=\"input-email-for-email-provider\"\n        type=\"email\"\n        name=\"email\"\n        placeholder=\"email@example.com\"\n        required\n      />\n    </slot>\n  {/if}\n  <button type=\"submit\">\n    <slot name=\"submitButton\">Signin{provider ? ` with ${provider}` : \"\"}</slot>\n  </button>\n</form>\n"
  },
  {
    "path": "packages/frameworks-sveltekit/src/lib/components/SignOut.svelte",
    "content": "<script lang=\"ts\">\n  import { enhance } from \"$app/forms\"\n  import type { signOut } from \"$lib/actions\"\n\n  export let className = \"\"\n  export let options: Parameters<typeof signOut>[0] = undefined\n  export let signOutPage = \"signout\"\n</script>\n\n<form\n  method=\"POST\"\n  action={`/${signOutPage}`}\n  use:enhance\n  class={`signOutButton ${className}`}\n  {...$$restProps}\n>\n  {#if options}\n    {#if options?.redirect}\n      <input type=\"hidden\" name=\"redirect\" value={options.redirect} />\n    {/if}\n    {#if options?.redirectTo}\n      <input type=\"hidden\" name=\"redirectTo\" value={options.redirectTo} />\n    {/if}\n  {/if}\n  <button type=\"submit\">\n    <slot name=\"submitButton\">Sign Out</slot>\n  </button>\n</form>\n"
  },
  {
    "path": "packages/frameworks-sveltekit/src/lib/components/index.ts",
    "content": "import SignIn from \"./SignIn.svelte\"\nimport SignOut from \"./SignOut.svelte\"\n\nexport { SignIn, SignOut }\n"
  },
  {
    "path": "packages/frameworks-sveltekit/src/lib/env.ts",
    "content": "import { setEnvDefaults as coreSetEnvDefaults, skipCSRFCheck } from \"@auth/core\"\nimport { dev, building } from \"$app/environment\"\nimport { base } from \"$app/paths\"\nimport type { SvelteKitAuthConfig } from \"./types\"\n\nexport function setEnvDefaults(\n  envObject: Record<string, string | undefined>,\n  config: SvelteKitAuthConfig\n) {\n  config.trustHost ??= dev\n  config.basePath = `${base}/auth`\n  config.skipCSRFCheck = skipCSRFCheck\n  if (building) return\n  coreSetEnvDefaults(envObject, config)\n}\n"
  },
  {
    "path": "packages/frameworks-sveltekit/src/lib/index.ts",
    "content": "/**\n * :::warning\n * `@auth/sveltekit` is currently experimental. The API _might_ change.\n * :::\n *\n * SvelteKit Auth is the official SvelteKit integration for Auth.js.\n * It provides a simple way to add authentication to your SvelteKit app in a few lines of code.\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install @auth/sveltekit\n * ```\n *\n * ## Usage\n *\n * ```ts title=\"src/auth.ts\"\n *\n * import { SvelteKitAuth } from \"@auth/sveltekit\"\n * import GitHub from \"@auth/sveltekit/providers/github\"\n *\n * export const { handle, signIn, signOut } = SvelteKitAuth({\n *   providers: [GitHub],\n * })\n * ```\n *\n * ### Lazy initialization\n * `@auth/sveltekit` supports lazy initialization where you can read the `event` object to lazily set the configuration. This is especially useful when you have to get the environment variables from `event.platform` for platforms like Cloudflare Workers.\n *\n * ```ts title=\"src/auth.ts\"\n * import { SvelteKitAuth } from \"@auth/sveltekit\"\n * import GitHub from \"@auth/sveltekit/providers/github\"\n *\n * export const { handle, signIn, signOut } = SvelteKitAuth(async (event) => {\n *   const authOptions = {\n *     providers: [\n *       GitHub({\n *         clientId: event.platform.env.AUTH_GITHUB_ID,\n *         clientSecret: event.platform.env.AUTH_GITHUB_SECRET\n *       })\n *     ],\n *     secret: event.platform.env.AUTH_SECRET,\n *     trustHost: true\n *   }\n *   return authOptions\n * })\n * ```\n *\n * Re-export the handle in `src/hooks.server.ts`:\n * ```ts title=\"src/hooks.server.ts\"\n * export { handle } from \"./auth\"\n * ```\n *\n * Remember to set the `AUTH_SECRET` [environment variable](https://kit.svelte.dev/docs/modules#$env-dynamic-private). This should be a minimum of 32 characters, random string. On UNIX systems you can use `openssl rand -hex 32` or check out `https://generate-secret.vercel.app/32`.\n *\n * When deploying your app outside Vercel, set the `AUTH_TRUST_HOST` variable to `true` for other hosting providers like Cloudflare Pages or Netlify.\n *\n * The callback URL used by the [providers](https://authjs.dev/getting-started/providers) must be set to the following, unless you override {@link SvelteKitAuthConfig.basePath}:\n *\n * ```\n * [origin]/auth/callback/[provider]\n * ```\n *\n * ## Signing in and Signing out\n *\n * ### Server-side\n *\n * `<SignIn />` and `<SignOut />` are components that `@auth/sveltekit` provides out of the box - they handle the sign-in/signout flow, and can be used as-is as a starting point or customized for your own components. This is an example of how to use the `SignIn` and `SignOut` components to login and logout using SvelteKit's server-side form actions. Another example is available on [our svelte-auth-example repo](https://github.com/nextauthjs/sveltekit-auth-example).\n *\n * You will need two things to make this work:\n *\n * 1. Using the components in your SvelteKit app's frontend (for instance `src/routes/+page.svelte`)\n * 2. Add the required `page.server.ts` at `/signin` (for `SignIn`) and `/signout` (for `SignOut`) to handle the form actions\n *\n * ```ts title=\"src/routes/+page.svelte\"\n * <script>\n *   import { SignIn, SignOut } from \"@auth/sveltekit/components\"\n *   import { page } from \"$app/stores\"\n * </script>\n *\n * <h1>SvelteKit Auth Example</h1>\n * <div>\n *   {#if $page.data.session}\n *     {#if $page.data.session.user?.image}\n *       <img\n *         src={$page.data.session.user.image}\n *         class=\"avatar\"\n *         alt=\"User Avatar\"\n *       />\n *     {/if}\n *     <span class=\"signedInText\">\n *       <small>Signed in as</small><br />\n *       <strong>{$page.data.session.user?.name ?? \"User\"}</strong>\n *     </span>\n *     <SignOut>\n *       <div slot=\"submitButton\" class=\"buttonPrimary\">Sign out</div>\n *     </SignOut>\n *   {:else}\n *     <span class=\"notSignedInText\">You are not signed in</span>\n *     <SignIn>\n *       <div slot=\"submitButton\" class=\"buttonPrimary\">Sign in</div>\n *     </SignIn>\n *     <SignIn provider=\"facebook\"/>\n *   {/if}\n * </div>\n * ```\n *\n * To set up the form actions, we need to define the files in `src/routes`:\n *\n * ```ts title=\"src/routes/signin/+page.server.ts\"\n * import { signIn } from \"../../auth\"\n * import type { Actions } from \"./$types\"\n * export const actions: Actions = { default: signIn }\n * ```\n *\n * ```ts title=\"src/routes/signout/+page.server.ts\"\n * import { signOut } from \"../../auth\"\n * import type { Actions } from \"./$types\"\n * export const actions: Actions = { default: signOut }\n * ```\n *\n * These routes are customizeable with the `signInPage` and `signOutPage` props on the respective comopnents.\n *\n * ### Client-Side\n *\n * We also export two methods from `@auth/sveltekit/client` in order to do client-side sign-in and sign-out actions.\n *\n * ```ts title=\"src/routes/+page.svelte\"\n * <script>\n *   import { signIn, signOut } from \"@auth/sveltekit/client\"\n *   let password\n * </script>\n *\n * <nav>\n *   <p>\n *     These actions are all using the methods exported from\n *     <code>@auth/sveltekit/client</code>\n *   </p>\n *   <div class=\"actions\">\n *     <div class=\"wrapper-form\">\n *       <button on:click={() => signIn(\"github\")}>Sign In with GitHub</button>\n *     </div>\n *     <div class=\"wrapper-form\">\n *       <button on:click={() => signIn(\"discord\")}>Sign In with Discord</button>\n *     </div>\n *     <div class=\"wrapper-form\">\n *       <div class=\"input-wrapper\">\n *         <label for=\"password\">Password</label>\n *         <input\n *           bind:value={password}\n *           type=\"password\"\n *           id=\"password\"\n *           name=\"password\"\n *           required\n *         />\n *       </div>\n *       <button on:click={() => signIn(\"credentials\", { password })}>\n *         Sign In with Credentials\n *       </button>\n *       <button on:click={() => signOut()}>\n *         Sign Out\n *       </button>\n *     </div>\n *   </div>\n * </nav>\n * ```\n *\n * ## Managing the session\n *\n * The above example checks for a session available in `$page.data.session`, however that needs to be set by us somewhere.\n * If you want this data to be available to all your routes you can add this to `src/routes/+layout.server.ts`.\n * The following code sets the session data in the `$page` store to be available to all routes.\n *\n * ```ts\n * import type { LayoutServerLoad } from './$types';\n *\n * export const load: LayoutServerLoad = async (event) => {\n *   return {\n *     session: await event.locals.auth()\n *   };\n * };\n * ```\n *\n * What you return in the function `LayoutServerLoad` will be available inside the `$page` store, in the `data` property: `$page.data`.\n * In this case we return an object with the `session` property which is what we are accessing in the other code paths.\n *\n * ## Handling authorization\n *\n * In SvelteKit there are a few ways you could protect routes from unauthenticated users.\n *\n * ### Per component\n *\n * The simplest case is protecting a single page, in which case you should put the logic in the `+page.server.ts` file.\n * Notice in this case that you could also `await event.parent` and grab the session from there, however this implementation works even if you haven't done the above in your root `+layout.server.ts`\n *\n * ```ts\n * import { redirect } from '@sveltejs/kit';\n * import type { PageServerLoad } from './$types';\n *\n * export const load: PageServerLoad = async (event) => {\n *   const session = await event.locals.auth();\n *   if (!session?.user) throw redirect(303, '/auth');\n *   return {};\n * };\n * ```\n *\n * :::danger\n * Make sure to ALWAYS grab the session information from the parent instead of using the store in the case of a `PageLoad`.\n * Not doing so can lead to users being able to incorrectly access protected information in the case the `+layout.server.ts` does not run for that page load.\n * For more information on SvelteKit's `load` functionality behaviour and its implications on authentication, see this [SvelteKit docs section](https://kit.svelte.dev/docs/load#implications-for-authentication).\n * :::\n *\n * You should NOT put authorization logic in a `+layout.server.ts` as the logic is not guaranteed to propagate to leafs in the tree.\n * Prefer to manually protect each route through the `+page.server.ts` file to avoid mistakes.\n * It is possible to force the layout file to run the load function on all routes, however that relies certain behaviours that can change and are not easily checked.\n * For more information about these caveats make sure to read this issue in the SvelteKit repository: https://github.com/sveltejs/kit/issues/6315\n *\n * ### Per path\n *\n * Another method that's possible for handling authorization is by restricting certain URIs from being available.\n * For many projects this is better because:\n * - This automatically protects actions and api routes in those URIs\n * - No code duplication between components\n * - Very easy to modify\n *\n * The way to handle authorization through the URI is to override your handle hook.\n * The handle hook, returned from `SvelteKitAuth` in your `src/auth.ts`, is a function that is designed to receive ALL requests sent to your SvelteKit webapp.\n * You should export it from `src/auth.ts` and import it in your `src/hooks.server.ts`.\n * To use multiple handles in your `hooks.server.ts`, we can use SvelteKit's `sequence` to execute all of them in series.\n *\n * ```ts title=\"src/auth.ts\"\n * import { SvelteKitAuth } from '@auth/sveltekit';\n * import GitHub from '@auth/sveltekit/providers/github';\n *\n * export const { handle, signIn, signOut } = SvelteKitAuth({\n *   providers: [GitHub]\n * }),\n * ```\n *\n * ```ts title=\"src/hooks.server.ts\"\n * import { redirect, type Handle } from '@sveltejs/kit';\n * import { handle as authenticationHandle } from './auth';\n * import { sequence } from '@sveltejs/kit/hooks';\n *\n * async function authorizationHandle({ event, resolve }) {\n *   // Protect any routes under /authenticated\n *   if (event.url.pathname.startsWith('/authenticated')) {\n *     const session = await event.locals.auth();\n *     if (!session) {\n *       // Redirect to the signin page\n *       throw redirect(303, '/auth/signin');\n *     }\n *   }\n *\n *   // If the request is still here, just proceed as normally\n *   return resolve(event);\n * }\n *\n * // First handle authentication, then authorization\n * // Each function acts as a middleware, receiving the request handle\n * // And returning a handle which gets passed to the next function\n * export const handle: Handle = sequence(authenticationHandle, authorizationHandle)\n * ```\n *\n * :::info\n * Learn more about SvelteKit's handle hooks and sequence [here](https://kit.svelte.dev/docs/modules#sveltejs-kit-hooks-sequence).\n * :::\n *\n * Now any routes under `/authenticated` will be transparently protected by the handle hook.\n * You may add more middleware-like functions to the sequence and also implement more complex authorization business logic inside this file.\n * This can also be used along with the component-based approach in case you need a specific page to be protected and doing it by URI could be faulty.\n *\n * ## Notes\n *\n * - If you build your SvelteKit application with `prerender` enabled, pages which have an anchor tag to the default signin page (i.e. `<a href=\"/auth/signin\" ...`) will have trouble building. Please use the [builtin functions or components](https://authjs.dev/getting-started/session-management/login?framework=sveltekit) to sign in or out instead.\n *\n * :::info\n * Learn more about `@auth/sveltekit` [here](https://vercel.com/blog/announcing-sveltekit-auth).\n * :::\n *\n * @module @auth/sveltekit\n */\n\nimport \"@sveltejs/kit\"\nimport type { Action, Handle, RequestEvent } from \"@sveltejs/kit\"\nimport { env } from \"$env/dynamic/private\"\n\nimport type { SvelteKitAuthConfig } from \"./types\"\nimport { setEnvDefaults } from \"./env\"\nimport { auth, signIn, signOut } from \"./actions\"\nimport { Auth, isAuthAction, customFetch } from \"@auth/core\"\nimport { building } from \"$app/environment\"\n\nexport { customFetch }\nexport { AuthError, CredentialsSignin } from \"@auth/core/errors\"\n\nexport type {\n  Account,\n  DefaultSession,\n  Profile,\n  Session,\n  User,\n} from \"@auth/core/types\"\n\nexport type { SvelteKitAuthConfig }\n\nconst authorizationParamsPrefix = \"authorizationParams-\"\n\n/**\n * The main entry point to `@auth/sveltekit`\n * @see https://sveltekit.authjs.dev\n */\nexport function SvelteKitAuth(\n  config:\n    | SvelteKitAuthConfig\n    | ((event: RequestEvent) => PromiseLike<SvelteKitAuthConfig>)\n): {\n  handle: Handle\n  signIn: Action\n  signOut: Action\n} {\n  return {\n    signIn: async (event) => {\n      if (building) return\n\n      const { request } = event\n      const _config = typeof config === \"object\" ? config : await config(event)\n      setEnvDefaults(env, _config)\n      const formData = await request.formData()\n      const { providerId: provider, ...options } = Object.fromEntries(formData)\n      // get the authorization params from the options prefixed with `authorizationParams-`\n      const authorizationParams: Parameters<typeof signIn>[2] = {}\n      const _options: Parameters<typeof signIn>[1] = {}\n      for (const key in options) {\n        if (key.startsWith(authorizationParamsPrefix)) {\n          authorizationParams[key.slice(authorizationParamsPrefix.length)] =\n            options[key] as string\n        } else {\n          _options[key] = options[key]\n        }\n      }\n      await signIn(\n        provider as string,\n        _options,\n        authorizationParams,\n        _config,\n        event\n      )\n    },\n    signOut: async (event) => {\n      if (building) return\n\n      const _config = typeof config === \"object\" ? config : await config(event)\n      setEnvDefaults(env, _config)\n      const options = Object.fromEntries(await event.request.formData())\n      await signOut(options, _config, event)\n    },\n    async handle({ event, resolve }) {\n      if (building) {\n        event.locals.auth ??= async () => null\n        event.locals.getSession ??= event.locals.auth\n        return resolve(event)\n      }\n\n      const _config = typeof config === \"object\" ? config : await config(event)\n      setEnvDefaults(env, _config)\n\n      const { url, request } = event\n\n      event.locals.auth ??= () => auth(event, _config)\n      event.locals.getSession ??= event.locals.auth\n\n      const action = url.pathname\n        .slice(\n          // @ts-expect-error - basePath is defined in setEnvDefaults\n          _config.basePath.length + 1\n        )\n        .split(\"/\")[0]\n\n      if (\n        isAuthAction(action) &&\n        url.pathname.startsWith(_config.basePath + \"/\")\n      ) {\n        return Auth(request, _config)\n      }\n      return resolve(event)\n    },\n  }\n}\n"
  },
  {
    "path": "packages/frameworks-sveltekit/src/lib/types.ts",
    "content": "import type { AuthConfig } from \"@auth/core\"\nimport type { ProviderId } from \"@auth/core/providers\"\nimport type { Session } from \"@auth/core/types\"\n\n/** Configure the {@link SvelteKitAuth} method. */\nexport interface SvelteKitAuthConfig extends Omit<AuthConfig, \"raw\"> {}\n\ndeclare global {\n  // eslint-disable-next-line @typescript-eslint/no-namespace\n  namespace App {\n    interface Locals {\n      auth(): Promise<Session | null>\n      /** @deprecated Use `auth` instead. */\n      getSession(): Promise<Session | null>\n      signIn: <Redirect extends boolean = true>(\n        /** Provider to sign in to */\n        provider?: ProviderId, // See: https://github.com/microsoft/TypeScript/issues/29729\n        options?:\n          | FormData\n          | ({\n              /** The URL to redirect to after signing in. By default, the user is redirected to the current page. */\n              redirectTo?: string\n              /** If set to `false`, the `signIn` method will return the URL to redirect to instead of redirecting automatically. */\n              redirect?: Redirect\n              // eslint-disable-next-line @typescript-eslint/no-explicit-any\n            } & Record<string, any>),\n        authorizationParams?:\n          | string[][]\n          | Record<string, string>\n          | string\n          | URLSearchParams\n      ) => Promise<\n        Redirect extends false\n          ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n            any\n          : never\n      >\n      signOut: <Redirect extends boolean = true>(options?: {\n        /** The URL to redirect to after signing out. By default, the user is redirected to the current page. */\n        redirectTo?: string\n        /** If set to `false`, the `signOut` method will return the URL to redirect to instead of redirecting automatically. */\n        redirect?: Redirect\n      }) => Promise<\n        Redirect extends false\n          ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n            any\n          : never\n      >\n    }\n    interface PageData {\n      session?: Session | null\n    }\n  }\n}\n\ndeclare module \"$env/dynamic/private\" {\n  export const AUTH_SECRET: string\n  export const AUTH_SECRET_1: string\n  export const AUTH_SECRET_2: string\n  export const AUTH_SECRET_3: string\n  export const AUTH_TRUST_HOST: string\n  export const VERCEL: string\n}\n"
  },
  {
    "path": "packages/frameworks-sveltekit/src/lib/webauthn.ts",
    "content": "import { base } from \"$app/paths\"\nimport { startAuthentication, startRegistration } from \"@simplewebauthn/browser\"\n\nimport type { LoggerInstance } from \"@auth/core/types\"\nimport type { WebAuthnOptionsResponseBody } from \"@auth/core/types\"\nimport type { ProviderId } from \"@auth/core/providers\"\nimport type {\n  SignInAuthorizationParams,\n  SignInOptions,\n  SignInResponse,\n} from \"./client.js\"\n\nconst logger: LoggerInstance = {\n  debug: console.debug,\n  error: console.error,\n  warn: console.warn,\n}\n\n/**\n * Fetch webauthn options from server and prompt user for authentication or registration.\n * Returns either the completed WebAuthn response or an error request.\n */\nasync function webAuthnOptions(\n  providerID: ProviderId,\n  options?: Omit<SignInOptions, \"redirect\">\n) {\n  const baseUrl = base ?? \"\"\n\n  // @ts-expect-error\n  const params = new URLSearchParams(options)\n\n  const optionsResp = await fetch(\n    `${baseUrl}/webauthn-options/${providerID}?${params}`\n  )\n  if (!optionsResp.ok) {\n    return { error: optionsResp }\n  }\n  const optionsData: WebAuthnOptionsResponseBody = await optionsResp.json()\n\n  if (optionsData.action === \"authenticate\") {\n    const webAuthnResponse = await startAuthentication(optionsData.options)\n    return { data: webAuthnResponse, action: \"authenticate\" }\n  } else {\n    const webAuthnResponse = await startRegistration(optionsData.options)\n    return { data: webAuthnResponse, action: \"register\" }\n  }\n}\n\n/**\n * Initiate a WebAuthn signin flow.\n * @see https://authjs.dev/getting-started/authentication/webauthn\n */\nexport async function signIn(\n  provider?: ProviderId,\n  options?: SignInOptions<true>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<void>\nexport async function signIn(\n  provider?: ProviderId,\n  options?: SignInOptions<false>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<SignInResponse>\nexport async function signIn<Redirect extends boolean = true>(\n  provider?: ProviderId,\n  options?: SignInOptions<Redirect>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<SignInResponse | void> {\n  const { callbackUrl, ...rest } = options ?? {}\n  const {\n    redirectTo = callbackUrl ?? window.location.href,\n    redirect = true,\n    ...signInParams\n  } = rest\n\n  const baseUrl = base ?? \"\"\n\n  if (!provider || provider !== \"webauthn\") {\n    // TODO: Add docs link with explanation\n    throw new TypeError(\n      [\n        `Provider id \"${provider}\" does not refer to a WebAuthn provider.`,\n        'Please use `import { signIn } from \"@auth/sveltekit/client\"` instead.',\n      ].join(\"\\n\")\n    )\n  }\n\n  const webAuthnBody: Record<string, unknown> = {}\n  const webAuthnResponse = await webAuthnOptions(provider, signInParams)\n  if (webAuthnResponse.error) {\n    logger.error(new Error(await webAuthnResponse.error.text()))\n    return\n  }\n  webAuthnBody.data = JSON.stringify(webAuthnResponse.data)\n  webAuthnBody.action = webAuthnResponse.action\n\n  const signInUrl = `${baseUrl}/callback/${provider}?${new URLSearchParams(authorizationParams)}`\n  const res = await fetch(signInUrl, {\n    method: \"post\",\n    headers: {\n      \"Content-Type\": \"application/x-www-form-urlencoded\",\n      \"X-Auth-Return-Redirect\": \"1\",\n    },\n    body: new URLSearchParams({\n      ...signInParams,\n      ...webAuthnBody,\n      callbackUrl: redirectTo,\n    }),\n  })\n\n  const data = await res.json()\n\n  if (redirect) {\n    const url = data.url ?? callbackUrl\n    window.location.href = url\n    // If url contains a hash, the browser does not reload the page. We reload manually\n    if (url.includes(\"#\")) window.location.reload()\n    return\n  }\n\n  const error = new URL(data.url).searchParams.get(\"error\")\n  const code = new URL(data.url).searchParams.get(\"code\")\n\n  return {\n    error,\n    code,\n    status: res.status,\n    ok: res.ok,\n    url: error ? null : data.url,\n  } as any\n}\n"
  },
  {
    "path": "packages/frameworks-sveltekit/test/index.test.ts",
    "content": "import { describe, it, expect } from \"vitest\"\n\ndescribe(\"SvelteKit\", () => {\n  it(\"should work\", () => {\n    expect(true).toBe(true)\n  })\n})\n"
  },
  {
    "path": "packages/frameworks-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    \"declaration\": true,\n    \"declarationMap\": true\n  },\n  // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias\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  \"exclude\": [\"scripts\", \"*.js\", \"../node_modules/**\", \"./[!ambient.d.ts]**\"]\n}\n"
  },
  {
    "path": "packages/frameworks-sveltekit/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/lib/index.ts\", \"src/lib/env.ts\", \"src/lib/client.ts\", \"src/lib/adapter.ts\", \"src/lib/actions.ts\", \"src/lib/types.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/sveltekit\",\n  entryFileName: \"../sveltekit.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/frameworks-template/README.md",
    "content": "<p align=\"center\">\n   <br/>\n   <a href=\"https://authjs.dev\" target=\"_blank\"><img width=\"150px\" src=\"https://authjs.dev/img/logo-sm.png\" /></a>\n   <h3 align=\"center\"><framework-name> Auth</a></h3>\n   <h4 align=\"center\">Authentication for <framework-name>.</h4>\n   <p align=\"center\" style=\"align: center;\">\n      <a href=\"https://npm.im/next-auth\">\n        <img src=\"https://img.shields.io/badge/TypeScript-blue?style=flat-square\" alt=\"TypeScript\" />\n      </a>\n      <a href=\"https://npm.im/@auth/<framework-id>\">\n        <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@auth/<framework-id>?color=green&label=@auth/<framework-id>&style=flat-square\">\n      </a>\n      <a href=\"https://www.npmtrends.com/@auth/<framework-id>\">\n        <img src=\"https://img.shields.io/npm/dm/@auth/<framework-id>?label=%20downloads&style=flat-square\" alt=\"Downloads\" />\n      </a>\n      <a href=\"https://github.com/nextauthjs/next-auth/stargazers\">\n        <img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square\" alt=\"GitHub Stars\" />\n      </a>\n   </p>\n</p>\n\n---\n\nCheck out the documentation at [<framework-id>.authjs.dev](https://<framework-id>.authjs.dev).\n"
  },
  {
    "path": "packages/frameworks-template/package.json",
    "content": "{\n  \"name\": \"@auth/frameworks-template\",\n  \"description\": \"Authentication for <framework-name>.\",\n  \"version\": \"0.3.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"files\": [\n    \"*.js\",\n    \"*.d.ts*\",\n    \"lib\",\n    \"providers\",\n    \"src\",\n    \"!vitest.config.js\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\"\n    },\n    \"./providers\": {\n      \"types\": \"./providers/index.d.ts\"\n    },\n    \"./providers/*\": {\n      \"types\": \"./providers/*.d.ts\",\n      \"import\": \"./providers/*.js\"\n    }\n  },\n  \"scripts\": {\n    \"build\": \"pnpm clean && pnpm providers && tsc\",\n    \"clean\": \"rm -rf lib index.* src/lib/providers\",\n    \"test\": \"vitest run\",\n    \"providers\": \"node ../utils/scripts/providers\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {},\n  \"keywords\": [\n    \"<framework-name>\",\n    \"oauth\",\n    \"jwt\",\n    \"oauth2\",\n    \"authentication\",\n    \"csrf\",\n    \"oidc\",\n    \"Auth.js\"\n  ],\n  \"author\": \"\",\n  \"contributors\": [],\n  \"repository\": \"https://github.com/nextauthjs/next-auth\",\n  \"license\": \"ISC\"\n}\n"
  },
  {
    "path": "packages/frameworks-template/src/index.ts",
    "content": "/**\n *\n * :::warning\n * `@auth/<framework-id>` is currently experimental. The API _will_ change in the future.\n * :::\n *\n * <framework-name> Auth is the official <framework-name> integration for Auth.js.\n * It provides a simple way to add authentication to your <framework-name> app in a few lines of code.\n *\n * ## Installation\n * ```bash npm2yarn\n * npm install @auth/<framework-id>\n * ```\n *\n * ## Usage\n *\n * ### Provider Configuration\n *\n * ## Signing in and signing out\n *\n * ## Managing the session\n *\n * ## Authorization\n *\n * ```\n *\n * @module @auth/<framework-id>\n */\n\n// Re-export types of Auth.js\nexport type {\n  Account,\n  DefaultSession,\n  Profile,\n  Session,\n  User,\n} from \"@auth/core/types\"\n\nexport function FrameworkAuth() {\n  throw new Error(\"Not implemented\")\n}\n"
  },
  {
    "path": "packages/frameworks-template/test/index.test.ts",
    "content": "import { describe, expect, it } from \"vitest\"\nimport { FrameworkAuth } from \"../src\"\n\ndescribe(\"Sample test\", () => {\n  it(\"should throw an error\", () => {\n    expect(() => {\n      FrameworkAuth()\n    }).toThrow(\"Not implemented\")\n  })\n})\n"
  },
  {
    "path": "packages/frameworks-template/test/test-setup.ts",
    "content": "globalThis.crypto ??= require(\"node:crypto\").webcrypto\n"
  },
  {
    "path": "packages/frameworks-template/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"*.js\", \"*.d.ts\", \"lib\"]\n}\n"
  },
  {
    "path": "packages/frameworks-template/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"@auth/<framework-id>\",\n  entryFileName: \"../<framework-id>.mdx\",\n  includeVersion: true,\n  readme: 'none',\n}\n"
  },
  {
    "path": "packages/next-auth/README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://nextjs.org\" target=\"_blank\"><img height=\"96px\" src=\"https://authjs.dev/img/etc/nextjs.svg\" /></a>\n  <a href=\"https://nextjs.authjs.dev\" target=\"_blank\"><img height=\"96px\" src=\"https://authjs.dev/img/logo-sm.png\" /></a>\n  <h1 align=\"center\">NextAuth.js</h1>\n</p>\n<p align=\"center\">\n  Authentication for Next.js.\n</p>\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/next-auth\"><img src=\"https://img.shields.io/npm/v/next-auth/beta?style=flat-square&label=latest&color=purple\" alt=\"npm beta release\" /></a>\n  <a href=\"https://www.npmtrends.com/next-auth\"><img src=\"https://img.shields.io/npm/dm/next-auth?style=flat-square&color=cyan\" alt=\"Downloads\" /></a>\n  <a href=\"https://github.com/nextauthjs/next-auth/stargazers\"><img src=\"https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square&color=orange\" alt=\"GitHub Stars\" /></a>\n  <img src=\"https://shields.io/badge/TypeScript-3178C6?logo=TypeScript&logoColor=fff&style=flat-square\" alt=\"TypeScript\" />\n</p>\n\n---\n\nCheck out the documentation at [nextjs.authjs.dev](https://nextjs.authjs.dev).\n"
  },
  {
    "path": "packages/next-auth/package.json",
    "content": "{\n  \"name\": \"next-auth\",\n  \"version\": \"5.0.0-beta.30\",\n  \"description\": \"Authentication for Next.js\",\n  \"homepage\": \"https://nextjs.authjs.dev\",\n  \"repository\": \"https://github.com/nextauthjs/next-auth.git\",\n  \"author\": \"Balázs Orbán <info@balazsorban.com>\",\n  \"contributors\": [\n    \"Iain Collins <me@iaincollins.com>\",\n    \"Nico Domino <yo@ndo.dev>\",\n    \"Lluis Agusti <hi@llu.lu>\",\n    \"Thang Huu Vu <hi@thvu.dev>\"\n  ],\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./index.d.ts\",\n      \"import\": \"./index.js\",\n      \"default\": \"./index.js\"\n    },\n    \"./jwt\": {\n      \"types\": \"./jwt.d.ts\",\n      \"import\": \"./jwt.js\",\n      \"default\": \"./jwt.js\"\n    },\n    \"./middleware\": {\n      \"types\": \"./middleware.d.ts\",\n      \"import\": \"./middleware.js\",\n      \"default\": \"./middleware.js\"\n    },\n    \"./next\": {\n      \"types\": \"./next.d.ts\",\n      \"import\": \"./next.js\",\n      \"default\": \"./next.js\"\n    },\n    \"./providers\": {\n      \"types\": \"./providers/index.d.ts\"\n    },\n    \"./adapters\": {\n      \"types\": \"./adapters.d.ts\"\n    },\n    \"./providers/*\": {\n      \"types\": \"./providers/*.d.ts\",\n      \"import\": \"./providers/*.js\",\n      \"default\": \"./providers/*.js\"\n    },\n    \"./react\": {\n      \"types\": \"./react.d.ts\",\n      \"import\": \"./react.js\",\n      \"default\": \"./react.js\"\n    },\n    \"./webauthn\": {\n      \"types\": \"./webauthn.d.ts\",\n      \"import\": \"./webauthn.js\",\n      \"default\": \"./webauthn.js\"\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"nodejs\",\n    \"oauth\",\n    \"jwt\",\n    \"oauth2\",\n    \"authentication\",\n    \"nextjs\",\n    \"csrf\",\n    \"oidc\",\n    \"nextauth\"\n  ],\n  \"scripts\": {\n    \"build\": \"pnpm clean && pnpm providers && tsc\",\n    \"clean\": \"rm -rf *.js *.d.ts* lib providers\",\n    \"dev\": \"pnpm providers && tsc -w\",\n    \"test\": \"vitest run -c ../utils/vitest.config.ts\",\n    \"test:watch\": \"vitest -c ../utils/vitest.config.ts\",\n    \"test:e2e\": \"playwright test\",\n    \"providers\": \"node ../utils/scripts/providers\"\n  },\n  \"files\": [\n    \"*.d.ts*\",\n    \"*.js\",\n    \"lib\",\n    \"providers\",\n    \"src\"\n  ],\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"@auth/core\": \"workspace:*\"\n  },\n  \"peerDependencies\": {\n    \"@simplewebauthn/browser\": \"^9.0.1\",\n    \"@simplewebauthn/server\": \"^9.0.2\",\n    \"next\": \"^14.0.0-0 || ^15.0.0 || ^16.0.0\",\n    \"nodemailer\": \"^7.0.7\",\n    \"react\": \"^18.2.0 || ^19.0.0\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@simplewebauthn/browser\": {\n      \"optional\": true\n    },\n    \"@simplewebauthn/server\": {\n      \"optional\": true\n    },\n    \"nodemailer\": {\n      \"optional\": true\n    }\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"18.0.37\",\n    \"dotenv\": \"^10.0.0\",\n    \"next\": \"15.3.1\",\n    \"nodemailer\": \"^7.0.7\",\n    \"react\": \"^18.2.0\"\n  }\n}\n"
  },
  {
    "path": "packages/next-auth/playwright.config.ts",
    "content": "// import { join } from \"node:path\"\nimport { defineConfig, devices } from \"@playwright/test\"\n// import * as dotenv from \"dotenv\"\n\n// Use process.env.PORT by default and fallback to port 3000\nconst PORT = process.env.PORT || 3000\n\n// Set webServer.url and use.baseURL with the location of the WebServer respecting the correct set port\nconst baseURL = `http://localhost:${PORT}`\n\n// if (!process.env.CI) {\n//   const path = process.env.TEST_DOCKER\n//     ? join(process.cwd(), \"..\", \"..\", \"..\", \"packages\", \"core\", \".env\")\n//     : join(process.cwd(), \"..\", \"core\", \".env\")\n\n//   dotenv.config({ path })\n// }\n\n/** See https://playwright.dev/docs/api/class-testconfig. */\nexport default defineConfig({\n  testDir: \"../\",\n  // Artifacts folder where screenshots, videos, and traces are stored.\n  outputDir: \"test-results/\",\n  testMatch: \"**/test/e2e/**/*.spec.ts\",\n  fullyParallel: true,\n  retries: process.env.CI ? 2 : 0,\n  workers: process.env.CI ? 1 : undefined,\n  reporter: process.env.CI\n    ? \"dot\"\n    : [[\"line\"], [\"html\", { open: \"on-failure\" }]],\n  use: {\n    // Use baseURL so to make navigations relative.\n    // More information: https://playwright.dev/docs/api/class-testoptions#test-options-base-url\n    baseURL,\n\n    // Retry a test if its failing with enabled tracing. This allows you to analyze the DOM, console logs, network traffic etc.\n    // More information: https://playwright.dev/docs/trace-viewer\n    trace: \"retry-with-trace\",\n  },\n  projects: [\n    {\n      name: \"chromium\",\n      use: { ...devices[\"Desktop Chrome\"] },\n    },\n  ],\n  webServer: {\n    cwd: \"../../apps/dev/nextjs\",\n    command: \"pnpm dev\",\n    url: baseURL,\n    stdout: \"pipe\",\n    reuseExistingServer: !process.env.CI,\n  },\n})\n"
  },
  {
    "path": "packages/next-auth/src/adapters.ts",
    "content": "export type * from \"@auth/core/adapters\"\n"
  },
  {
    "path": "packages/next-auth/src/index.ts",
    "content": "/**\n * _If you are looking to migrate from v4, visit the [Upgrade Guide (v5)](https://authjs.dev/getting-started/migrating-to-v5)._\n *\n * ## Installation\n *\n * ```bash npm2yarn\n * npm install next-auth@beta\n * ```\n *\n * ## Environment variable inference\n *\n * `NEXTAUTH_URL` and `NEXTAUTH_SECRET` have been inferred since v4.\n *\n * Since NextAuth.js v5 can also automatically infer environment variables that are prefixed with `AUTH_`.\n *\n * For example `AUTH_GITHUB_ID` and `AUTH_GITHUB_SECRET` will be used as the `clientId` and `clientSecret` options for the GitHub provider.\n *\n * :::tip\n * The environment variable name inferring has the following format for OAuth providers: `AUTH_{PROVIDER}_{ID|SECRET}`.\n *\n * `PROVIDER` is the uppercase snake case version of the provider's id, followed by either `ID` or `SECRET` respectively.\n * :::\n *\n * `AUTH_SECRET` and `AUTH_URL` are also aliased for `NEXTAUTH_SECRET` and `NEXTAUTH_URL` for consistency.\n *\n * To add social login to your app, the configuration becomes:\n *\n * ```ts title=\"auth.ts\"\n * import NextAuth from \"next-auth\"\n * import GitHub from \"next-auth/providers/github\"\n * export const { handlers, auth } = NextAuth({ providers: [ GitHub ] })\n * ```\n *\n * And the `.env.local` file:\n *\n * ```sh title=\".env.local\"\n * AUTH_GITHUB_ID=...\n * AUTH_GITHUB_SECRET=...\n * AUTH_SECRET=...\n * ```\n *\n * :::tip\n * In production, `AUTH_SECRET` is a required environment variable - if not set, NextAuth.js will throw an error. See [MissingSecretError](https://authjs.dev/reference/core/errors#missingsecret) for more details.\n * :::\n *\n * If you need to override the default values for a provider, you can still call it as a function `GitHub({...})` as before.\n *\n * ## Lazy initialization\n * You can also initialize NextAuth.js lazily (previously known as advanced intialization), which allows you to access the request context in the configuration in some cases, like Route Handlers, Middleware, API Routes or `getServerSideProps`.\n * The above example becomes:\n *\n * ```ts title=\"auth.ts\"\n * import NextAuth from \"next-auth\"\n * import GitHub from \"next-auth/providers/github\"\n * export const { handlers, auth } = NextAuth(req => {\n *  if (req) {\n *   console.log(req) // do something with the request\n *  }\n *  return { providers: [ GitHub ] }\n * })\n * ```\n *\n * :::tip\n * This is useful if you want to customize the configuration based on the request, for example, to add a different provider in staging/dev environments.\n * :::\n *\n * @module next-auth\n */\n\nimport { Auth, customFetch } from \"@auth/core\"\nimport { reqWithEnvURL, setEnvDefaults } from \"./lib/env.js\"\nimport { initAuth } from \"./lib/index.js\"\nimport { signIn, signOut, update } from \"./lib/actions.js\"\n\nimport type { Awaitable, Session } from \"@auth/core/types\"\nimport type { ProviderId } from \"@auth/core/providers\"\nimport type {\n  GetServerSidePropsContext,\n  NextApiRequest,\n  NextApiResponse,\n} from \"next\"\nimport type {\n  AppRouteHandlerFn,\n  AppRouteHandlerFnContext,\n} from \"./lib/types.js\"\n// @ts-expect-error Next.js does not yet correctly use the `package.json#exports` field\nimport type { NextRequest, NextMiddleware } from \"next/server\"\nimport type {\n  NextAuthConfig,\n  NextAuthRequest,\n  NextAuthMiddleware,\n} from \"./lib/index.js\"\nexport { AuthError, CredentialsSignin } from \"@auth/core/errors\"\n\nexport { customFetch }\n\nexport type {\n  Session,\n  Account,\n  Profile,\n  DefaultSession,\n  User,\n} from \"@auth/core/types\"\n\ntype AppRouteHandlers = Record<\n  \"GET\" | \"POST\",\n  (req: NextRequest) => Promise<Response>\n>\n\nexport type { NextAuthConfig, NextAuthRequest }\n\n/**\n * The result of invoking {@link NextAuth|NextAuth}, initialized with the {@link NextAuthConfig}.\n * It contains methods to set up and interact with NextAuth.js in your Next.js app.\n */\nexport interface NextAuthResult {\n  /**\n   * The NextAuth.js [Route Handler](https://beta.nextjs.org/docs/routing/route-handlers) methods. These are used to expose an endpoint for OAuth/Email providers,\n   * as well as REST API endpoints (such as `/api/auth/session`) that can be contacted from the client.\n   *\n   * After initializing NextAuth.js in `auth.ts`,\n   * re-export these methods.\n   *\n   * In `app/api/auth/[...nextauth]/route.ts`:\n   *\n   * ```ts title=\"app/api/auth/[...nextauth]/route.ts\"\n   * export { GET, POST } from \"../../../../auth\"\n   * export const runtime = \"edge\" // optional\n   * ```\n   * Then `auth.ts`:\n   * ```ts title=\"auth.ts\"\n   * // ...\n   * export const { handlers: { GET, POST }, auth } = NextAuth({...})\n   * ```\n   */\n  handlers: AppRouteHandlers\n  /**\n   * A universal method to interact with NextAuth.js in your Next.js app.\n   * After initializing NextAuth.js in `auth.ts`, use this method in Middleware, Server Components, Route Handlers (`app/`), and Edge or Node.js API Routes (`pages/`).\n   *\n   * ##### In Middleware\n   *\n   * :::info\n   * Adding `auth` to your Middleware is optional, but recommended to keep the user session alive.\n   * :::\n   *\n   * Authentication is done by the {@link NextAuthConfig.callbacks|callbacks.authorized} callback.\n   * @example\n   * ```ts title=\"middleware.ts\"\n   * export { auth as middleware } from \"./auth\"\n   * ```\n   *\n   * Alternatively you can wrap your own middleware with `auth`, where `req` is extended with `auth`:\n   * @example\n   * ```ts title=\"middleware.ts\"\n   * import { auth } from \"./auth\"\n   * export default auth((req) => {\n   *   // req.auth\n   * })\n   * ```\n   *\n   * ```ts\n   * // Optionally, don't invoke Middleware on some paths\n   * // Read more: https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher\n   * export const config = {\n   *   matcher: [\"/((?!api|_next/static|_next/image|favicon.ico).*)\"],\n   * }\n   * ```\n   *\n   * ##### In Server Components\n   *\n   * @example\n   * ```ts title=\"app/page.ts\"\n   * import { auth } from \"../auth\"\n   *\n   * export default async function Page() {\n   *   const { user } = await auth()\n   *   return <p>Hello {user?.name}</p>\n   * }\n   * ```\n   *\n   * ##### In Route Handlers\n   * @example\n   * ```ts title=\"app/api/route.ts\"\n   * import { auth } from \"../../auth\"\n   *\n   * export const POST = auth((req) => {\n   *   // req.auth\n   * })\n   * ```\n   *\n   * ##### In Edge API Routes\n   *\n   * @example\n   * ```ts title=\"pages/api/protected.ts\"\n   * import { auth } from \"../../auth\"\n   *\n   * export default auth((req) => {\n   *   // req.auth\n   * })\n   *\n   * export const config = { runtime: \"edge\" }\n   * ```\n   *\n   * ##### In API Routes\n   *\n   * @example\n   * ```ts title=\"pages/api/protected.ts\"\n   * import { auth } from \"../auth\"\n   * import type { NextApiRequest, NextApiResponse } from \"next\"\n   *\n   * export default async (req: NextApiRequest, res: NextApiResponse) => {\n   *   const session = await auth(req, res)\n   *   if (session) {\n   *     // Do something with the session\n   *     return res.json(\"This is protected content.\")\n   *   }\n   *   res.status(401).json(\"You must be signed in.\")\n   * }\n   * ```\n   *\n   * ##### In `getServerSideProps`\n   *\n   * @example\n   * ```ts title=\"pages/protected-ssr.ts\"\n   * import { auth } from \"../auth\"\n   *\n   * export const getServerSideProps: GetServerSideProps = async (context) => {\n   *   const session = await auth(context)\n   *\n   *   if (session) {\n   *     // Do something with the session\n   *     return { props: { session, content: (await res.json()).content } }\n   *   }\n   *\n   *   return { props: {} }\n   * }\n   * ```\n   */\n  auth: ((\n    ...args: [NextApiRequest, NextApiResponse]\n  ) => Promise<Session | null>) &\n    ((...args: []) => Promise<Session | null>) &\n    ((...args: [GetServerSidePropsContext]) => Promise<Session | null>) &\n    ((\n      ...args: [\n        (\n          req: NextAuthRequest,\n          ctx: AppRouteHandlerFnContext\n        ) => ReturnType<AppRouteHandlerFn>,\n      ]\n    ) => AppRouteHandlerFn) &\n    ((...args: [NextAuthMiddleware]) => NextMiddleware)\n  /**\n   * Sign in with a provider. If no provider is specified, the user will be redirected to the sign in page.\n   *\n   * By default, the user is redirected to the current page after signing in. You can override this behavior by setting the `redirectTo` option with a relative path.\n   *\n   * @example\n   * ```ts title=\"app/layout.tsx\"\n   * import { signIn } from \"../auth\"\n   *\n   * export default function Layout() {\n   *  return (\n   *   <form action={async () => {\n   *     \"use server\"\n   *     await signIn(\"github\")\n   *   }}>\n   *    <button>Sign in with GitHub</button>\n   *   </form>\n   * )\n   * ```\n   *\n   * If an error occurs during signin, an instance of {@link AuthError} will be thrown. You can catch it like this:\n   * ```ts title=\"app/layout.tsx\"\n   * import { AuthError } from \"next-auth\"\n   * import { signIn } from \"../auth\"\n   *\n   * export default function Layout() {\n   *  return (\n   *    <form action={async (formData) => {\n   *      \"use server\"\n   *      try {\n   *        await signIn(\"credentials\", formData)\n   *     } catch(error) {\n   *       if (error instanceof AuthError) // Handle auth errors\n   *       throw error // Rethrow all other errors\n   *     }\n   *    }}>\n   *     <button>Sign in</button>\n   *   </form>\n   *  )\n   * }\n   * ```\n   *\n   */\n  signIn: <P extends ProviderId, R extends boolean = true>(\n    /** Provider to sign in to */\n    provider?: P, // See: https://github.com/microsoft/TypeScript/issues/29729\n    options?:\n      | FormData\n      | ({\n          /** The relative path to redirect to after signing in. By default, the user is redirected to the current page. */\n          redirectTo?: string\n          /** If set to `false`, the `signIn` method will return the URL to redirect to instead of redirecting automatically. */\n          redirect?: R\n        } & Record<string, any>),\n    authorizationParams?:\n      | string[][]\n      | Record<string, string>\n      | string\n      | URLSearchParams\n  ) => Promise<R extends false ? any : never>\n  /**\n   * Sign out the user. If the session was created using a database strategy, the session will be removed from the database and the related cookie is invalidated.\n   * If the session was created using a JWT, the cookie is invalidated.\n   *\n   * By default the user is redirected to the current page after signing out. You can override this behavior by setting the `redirectTo` option with a relative path.\n   *\n   * @example\n   * ```ts title=\"app/layout.tsx\"\n   * import { signOut } from \"../auth\"\n   *\n   * export default function Layout() {\n   *  return (\n   *   <form action={async () => {\n   *     \"use server\"\n   *     await signOut()\n   *   }}>\n   *    <button>Sign out</button>\n   *   </form>\n   * )\n   * ```\n   *\n   *\n   */\n  signOut: <R extends boolean = true>(options?: {\n    /** The relative path to redirect to after signing out. By default, the user is redirected to the current page. */\n    redirectTo?: string\n    /** If set to `false`, the `signOut` method will return the URL to redirect to instead of redirecting automatically. */\n    redirect?: R\n  }) => Promise<R extends false ? any : never>\n  unstable_update: (\n    data: Partial<Session | { user: Partial<Session[\"user\"]> }>\n  ) => Promise<Session | null>\n}\n\n/**\n *  Initialize NextAuth.js.\n *\n *  @example\n * ```ts title=\"auth.ts\"\n * import NextAuth from \"next-auth\"\n * import GitHub from \"@auth/core/providers/github\"\n *\n * export const { handlers, auth } = NextAuth({ providers: [GitHub] })\n * ```\n *\n * Lazy initialization:\n *\n * @example\n * ```ts title=\"auth.ts\"\n * import NextAuth from \"next-auth\"\n * import GitHub from \"@auth/core/providers/github\"\n *\n * export const { handlers, auth } = NextAuth(async (req) => {\n *   console.log(req) // do something with the request\n *   return {\n *     providers: [GitHub],\n *   },\n * })\n * ```\n */\nexport default function NextAuth(\n  config:\n    | NextAuthConfig\n    | ((request: NextRequest | undefined) => Awaitable<NextAuthConfig>)\n): NextAuthResult {\n  if (typeof config === \"function\") {\n    const httpHandler = async (req: NextRequest) => {\n      const _config = await config(req)\n      setEnvDefaults(_config)\n      return Auth(reqWithEnvURL(req), _config)\n    }\n\n    return {\n      handlers: { GET: httpHandler, POST: httpHandler } as const,\n      // @ts-expect-error\n      auth: initAuth(config, (c) => setEnvDefaults(c)),\n\n      signIn: async (provider, options, authorizationParams) => {\n        const _config = await config(undefined)\n        setEnvDefaults(_config)\n        return signIn(provider, options, authorizationParams, _config)\n      },\n      signOut: async (options) => {\n        const _config = await config(undefined)\n        setEnvDefaults(_config)\n        return signOut(options, _config)\n      },\n      unstable_update: async (data) => {\n        const _config = await config(undefined)\n        setEnvDefaults(_config)\n        return update(data, _config)\n      },\n    }\n  }\n  setEnvDefaults(config)\n  const httpHandler = (req: NextRequest) => Auth(reqWithEnvURL(req), config)\n  return {\n    handlers: { GET: httpHandler, POST: httpHandler } as const,\n    // @ts-expect-error\n    auth: initAuth(config),\n    signIn: (provider, options, authorizationParams) => {\n      return signIn(provider, options, authorizationParams, config)\n    },\n    signOut: (options) => {\n      return signOut(options, config)\n    },\n    unstable_update: (data) => {\n      return update(data, config)\n    },\n  }\n}\n"
  },
  {
    "path": "packages/next-auth/src/jwt.ts",
    "content": "/**\n * :::warning Not recommended\n * In NextAuth.js v5 or newer, we recommend other authentication methods server-side. Read more at: https://authjs.dev/getting-started/migrating-to-v5#authenticating-server-side\n * :::\n *\n * @module jwt\n */\n\nexport * from \"@auth/core/jwt\"\n"
  },
  {
    "path": "packages/next-auth/src/lib/actions.ts",
    "content": "import { Auth, raw, skipCSRFCheck, createActionURL } from \"@auth/core\"\n// @ts-expect-error Next.js does not yet correctly use the `package.json#exports` field\nimport { headers as nextHeaders, cookies } from \"next/headers\"\n// @ts-expect-error Next.js does not yet correctly use the `package.json#exports` field\nimport { redirect } from \"next/navigation\"\n\nimport type { NextAuthConfig } from \"./index.js\"\nimport type { NextAuthResult, Session } from \"../index.js\"\nimport type { ProviderType } from \"@auth/core/providers\"\n\ntype SignInParams = Parameters<NextAuthResult[\"signIn\"]>\nexport async function signIn(\n  provider: SignInParams[0],\n  options: SignInParams[1] = {},\n  authorizationParams: SignInParams[2],\n  config: NextAuthConfig\n) {\n  const headers = new Headers(await nextHeaders())\n  const {\n    redirect: shouldRedirect = true,\n    redirectTo,\n    ...rest\n  } = options instanceof FormData ? Object.fromEntries(options) : options\n\n  const callbackUrl = redirectTo?.toString() ?? headers.get(\"Referer\") ?? \"/\"\n  const signInURL = createActionURL(\n    \"signin\",\n    // @ts-expect-error `x-forwarded-proto` is not nullable, next.js sets it by default\n    headers.get(\"x-forwarded-proto\"),\n    headers,\n    process.env,\n    config\n  )\n\n  if (!provider) {\n    signInURL.searchParams.append(\"callbackUrl\", callbackUrl)\n    if (shouldRedirect) redirect(signInURL.toString())\n    return signInURL.toString()\n  }\n\n  let url = `${signInURL}/${provider}?${new URLSearchParams(\n    authorizationParams\n  )}`\n  let foundProvider: { id?: SignInParams[0]; type?: ProviderType } = {}\n\n  for (const providerConfig of config.providers) {\n    const { options, ...defaults } =\n      typeof providerConfig === \"function\" ? providerConfig() : providerConfig\n    const id = (options?.id as string | undefined) ?? defaults.id\n    if (id === provider) {\n      foundProvider = {\n        id,\n        type: (options?.type as ProviderType | undefined) ?? defaults.type,\n      }\n      break\n    }\n  }\n\n  if (!foundProvider.id) {\n    const url = `${signInURL}?${new URLSearchParams({ callbackUrl })}`\n    if (shouldRedirect) redirect(url)\n    return url\n  }\n\n  if (foundProvider.type === \"credentials\") {\n    url = url.replace(\"signin\", \"callback\")\n  }\n\n  headers.set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n  const body = new URLSearchParams({ ...rest, callbackUrl })\n  const req = new Request(url, { method: \"POST\", headers, body })\n  const res = await Auth(req, { ...config, raw, skipCSRFCheck })\n\n  const cookieJar = await cookies()\n  for (const c of res?.cookies ?? []) cookieJar.set(c.name, c.value, c.options)\n\n  const responseUrl =\n    res instanceof Response ? res.headers.get(\"Location\") : res.redirect\n\n  // NOTE: if for some unexpected reason the responseUrl is not set,\n  // we redirect to the original url\n  const redirectUrl = responseUrl ?? url\n\n  if (shouldRedirect) return redirect(redirectUrl)\n\n  return redirectUrl as any\n}\n\ntype SignOutParams = Parameters<NextAuthResult[\"signOut\"]>\nexport async function signOut(\n  options: SignOutParams[0],\n  config: NextAuthConfig\n) {\n  const headers = new Headers(await nextHeaders())\n  headers.set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\n  const url = createActionURL(\n    \"signout\",\n    // @ts-expect-error `x-forwarded-proto` is not nullable, next.js sets it by default\n    headers.get(\"x-forwarded-proto\"),\n    headers,\n    process.env,\n    config\n  )\n  const callbackUrl = options?.redirectTo ?? headers.get(\"Referer\") ?? \"/\"\n  const body = new URLSearchParams({ callbackUrl })\n  const req = new Request(url, { method: \"POST\", headers, body })\n\n  const res = await Auth(req, { ...config, raw, skipCSRFCheck })\n\n  const cookieJar = await cookies()\n  for (const c of res?.cookies ?? []) cookieJar.set(c.name, c.value, c.options)\n\n  if (options?.redirect ?? true) return redirect(res.redirect!)\n\n  return res as any\n}\n\ntype UpdateParams = Parameters<NextAuthResult[\"unstable_update\"]>\nexport async function update(\n  data: UpdateParams[0],\n  config: NextAuthConfig\n): Promise<Session | null> {\n  const headers = new Headers(await nextHeaders())\n  headers.set(\"Content-Type\", \"application/json\")\n\n  const url = createActionURL(\n    \"session\",\n    // @ts-expect-error `x-forwarded-proto` is not nullable, next.js sets it by default\n    headers.get(\"x-forwarded-proto\"),\n    headers,\n    process.env,\n    config\n  )\n  const body = JSON.stringify({ data })\n  const req = new Request(url, { method: \"POST\", headers, body })\n\n  const res: any = await Auth(req, { ...config, raw, skipCSRFCheck })\n\n  const cookieJar = await cookies()\n  for (const c of res?.cookies ?? []) cookieJar.set(c.name, c.value, c.options)\n\n  return res.body\n}\n"
  },
  {
    "path": "packages/next-auth/src/lib/client.ts",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport type { ProviderId, ProviderType } from \"@auth/core/providers\"\nimport type { LoggerInstance, Session } from \"@auth/core/types\"\nimport { AuthError } from \"@auth/core/errors\"\n\n/** @todo */\nclass ClientFetchError extends AuthError {}\n\n/** @todo */\nexport class ClientSessionError extends AuthError {}\n\nexport interface AuthClientConfig {\n  baseUrl: string\n  basePath: string\n  baseUrlServer: string\n  basePathServer: string\n  /** Stores last session response */\n  _session?: Session | null | undefined\n  /** Used for timestamp since last sycned (in seconds) */\n  _lastSync: number\n  /**\n   * Stores the `SessionProvider`'s session update method to be able to\n   * trigger session updates from places like `signIn` or `signOut`\n   */\n  _getSession: (...args: any[]) => any\n}\n\nexport interface UseSessionOptions<R extends boolean> {\n  required: R\n  /** Defaults to `signIn` */\n  onUnauthenticated?: () => void\n}\n\nexport interface ClientSafeProvider {\n  id: ProviderId\n  name: string\n  type: ProviderType\n  signinUrl: string\n  callbackUrl: string\n  redirectTo: string\n}\n\nexport interface SignInOptions<Redirect extends boolean = true>\n  extends Record<string, unknown> {\n  /** @deprecated Use `redirectTo` instead. */\n  callbackUrl?: string\n  /**\n   * Specify where the user should be redirected to after a successful signin.\n   *\n   * By default, it is the page the sign-in was initiated from.\n   */\n  redirectTo?: string\n  /**\n   * You might want to deal with the signin response on the same page, instead of redirecting to another page.\n   * For example, if an error occurs (like wrong credentials given by the user), you might want to show an inline error message on the input field.\n   *\n   * For this purpose, you can set this to option `redirect: false`.\n   */\n  redirect?: Redirect\n}\n\nexport interface SignInResponse {\n  error: string | undefined\n  code: string | undefined\n  status: number\n  ok: boolean\n  url: string | null\n}\n\n/**\n * Match `inputType` of `new URLSearchParams(inputType)`\n * @internal\n */\nexport type SignInAuthorizationParams =\n  | string\n  | string[][]\n  | Record<string, string>\n  | URLSearchParams\n\n/** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1) */\nexport interface SignOutResponse {\n  url: string\n}\n\nexport interface SignOutParams<Redirect extends boolean = true> {\n  /** @deprecated Use `redirectTo` instead. */\n  callbackUrl?: string\n  /**\n   * If you pass `redirect: false`, the page will not reload.\n   * The session will be deleted, and `useSession` is notified, so any indication about the user will be shown as logged out automatically.\n   * It can give a very nice experience for the user.\n   */\n  redirectTo?: string\n  /** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1 */\n  redirect?: Redirect\n}\n\n/**\n \n * If you have session expiry times of 30 days (the default) or more, then you probably don't need to change any of the default options.\n *\n * However, if you need to customize the session behavior and/or are using short session expiry times, you can pass options to the provider to customize the behavior of the {@link useSession} hook.\n */\nexport interface SessionProviderProps {\n  children: React.ReactNode\n  session?: Session | null\n  baseUrl?: string\n  basePath?: string\n  /**\n   * A time interval (in seconds) after which the session will be re-fetched.\n   * If set to `0` (default), the session is not polled.\n   */\n  refetchInterval?: number\n  /**\n   * `SessionProvider` automatically refetches the session when the user switches between windows.\n   * This option activates this behaviour if set to `true` (default).\n   */\n  refetchOnWindowFocus?: boolean\n  /**\n   * Set to `false` to stop polling when the device has no internet access offline (determined by `navigator.onLine`)\n   *\n   * [`navigator.onLine` documentation](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine)\n   */\n  refetchWhenOffline?: false\n}\n\n// ------------------------ Internal ------------------------\n\n/**\n * If passed 'appContext' via getInitialProps() in _app.js\n * then get the req object from ctx and use that for the\n * req value to allow `fetchData` to\n * work seemlessly in getInitialProps() on server side\n * pages *and* in _app.js.\n * @internal\n */\nexport async function fetchData<T = any>(\n  path: string,\n  __NEXTAUTH: AuthClientConfig,\n  logger: LoggerInstance,\n  req: any = {}\n): Promise<T | null> {\n  const url = `${apiBaseUrl(__NEXTAUTH)}/${path}`\n  try {\n    const options: RequestInit = {\n      headers: {\n        \"Content-Type\": \"application/json\",\n        ...(req?.headers?.cookie ? { cookie: req.headers.cookie } : {}),\n      },\n    }\n\n    if (req?.body) {\n      options.body = JSON.stringify(req.body)\n      options.method = \"POST\"\n    }\n\n    const res = await fetch(url, options)\n    const data = await res.json()\n    if (!res.ok) throw data\n    return data\n  } catch (error) {\n    logger.error(new ClientFetchError((error as Error).message, error as any))\n    return null\n  }\n}\n\n/** @internal */\nexport function apiBaseUrl(__NEXTAUTH: AuthClientConfig) {\n  if (typeof window === \"undefined\") {\n    // Return absolute path when called server side\n    return `${__NEXTAUTH.baseUrlServer}${__NEXTAUTH.basePathServer}`\n  }\n  // Return relative path when called client side\n  return __NEXTAUTH.basePath\n}\n\n/** @internal  */\nexport function useOnline() {\n  const [isOnline, setIsOnline] = React.useState(\n    typeof navigator !== \"undefined\" ? navigator.onLine : false\n  )\n\n  const setOnline = () => setIsOnline(true)\n  const setOffline = () => setIsOnline(false)\n\n  React.useEffect(() => {\n    window.addEventListener(\"online\", setOnline)\n    window.addEventListener(\"offline\", setOffline)\n\n    return () => {\n      window.removeEventListener(\"online\", setOnline)\n      window.removeEventListener(\"offline\", setOffline)\n    }\n  }, [])\n\n  return isOnline\n}\n\n/**\n * Returns the number of seconds elapsed since January 1, 1970 00:00:00 UTC.\n * @internal\n */\nexport function now() {\n  return Math.floor(Date.now() / 1000)\n}\n\n/**\n * Returns an `URL` like object to make requests/redirects from server-side\n * @internal\n */\nexport function parseUrl(url?: string): {\n  /** @default \"http://localhost:3000\" */\n  origin: string\n  /** @default \"localhost:3000\" */\n  host: string\n  /** @default \"/api/auth\" */\n  path: string\n  /** @default \"http://localhost:3000/api/auth\" */\n  base: string\n  /** @default \"http://localhost:3000/api/auth\" */\n  toString: () => string\n} {\n  const defaultUrl = new URL(\"http://localhost:3000/api/auth\")\n\n  if (url && !url.startsWith(\"http\")) {\n    url = `https://${url}`\n  }\n\n  const _url = new URL(url || defaultUrl)\n  const path = (_url.pathname === \"/\" ? defaultUrl.pathname : _url.pathname)\n    // Remove trailing slash\n    .replace(/\\/$/, \"\")\n\n  const base = `${_url.origin}${path}`\n\n  return {\n    origin: _url.origin,\n    host: _url.host,\n    path,\n    base,\n    toString: () => base,\n  }\n}\n"
  },
  {
    "path": "packages/next-auth/src/lib/env.ts",
    "content": "// @ts-expect-error Next.js does not yet correctly use the `package.json#exports` field\nimport { NextRequest } from \"next/server\"\nimport type { NextAuthConfig } from \"./index.js\"\nimport { setEnvDefaults as coreSetEnvDefaults } from \"@auth/core\"\n\n/** If `NEXTAUTH_URL` or `AUTH_URL` is defined, override the request's URL. */\nexport function reqWithEnvURL(req: NextRequest): NextRequest {\n  const url = process.env.AUTH_URL ?? process.env.NEXTAUTH_URL\n  if (!url) return req\n  const { origin: envOrigin } = new URL(url)\n  const { href, origin } = req.nextUrl\n  return new NextRequest(href.replace(origin, envOrigin), req)\n}\n\n/**\n * For backwards compatibility, `next-auth` checks for `NEXTAUTH_URL`\n * and the `basePath` by default is `/api/auth` instead of `/auth`\n * (which is the default for all other Auth.js integrations).\n *\n * For the same reason, `NEXTAUTH_SECRET` is also checked.\n */\nexport function setEnvDefaults(config: NextAuthConfig) {\n  try {\n    config.secret ??= process.env.AUTH_SECRET ?? process.env.NEXTAUTH_SECRET\n    const url = process.env.AUTH_URL ?? process.env.NEXTAUTH_URL\n    if (!url) return\n    const { pathname } = new URL(url)\n    if (pathname === \"/\") return\n    config.basePath ||= pathname\n  } catch {\n    // Catching and swallowing potential URL parsing errors, we'll fall\n    // back to `/api/auth` below.\n  } finally {\n    config.basePath ||= \"/api/auth\"\n    coreSetEnvDefaults(process.env, config, true)\n  }\n}\n"
  },
  {
    "path": "packages/next-auth/src/lib/index.ts",
    "content": "import { Auth, createActionURL, type AuthConfig } from \"@auth/core\"\n// @ts-expect-error Next.js does not yet correctly use the `package.json#exports` field\nimport { headers } from \"next/headers\"\n// @ts-expect-error Next.js does not yet correctly use the `package.json#exports` field\nimport { NextResponse } from \"next/server\"\nimport { reqWithEnvURL } from \"./env.js\"\n\nimport type { AuthAction, Awaitable, Session } from \"@auth/core/types\"\nimport type {\n  GetServerSidePropsContext,\n  NextApiRequest,\n  NextApiResponse,\n} from \"next\"\nimport type { AppRouteHandlerFn } from \"./types.js\"\n// @ts-expect-error Next.js does not yet correctly use the `package.json#exports` field\nimport type { NextFetchEvent, NextMiddleware, NextRequest } from \"next/server\"\n\n/** Configure NextAuth.js. */\nexport interface NextAuthConfig extends Omit<AuthConfig, \"raw\"> {\n  /**\n   * Callbacks are asynchronous functions you can use to control what happens when an auth-related action is performed.\n   * Callbacks **allow you to implement access controls without a database** or to **integrate with external databases or APIs**.\n   */\n  callbacks?: AuthConfig[\"callbacks\"] & {\n    /**\n     * Invoked when a user needs authorization, using [Middleware](https://nextjs.org/docs/advanced-features/middleware).\n     *\n     * You can override this behavior by returning a {@link NextResponse}.\n     *\n     * @example\n     * ```ts title=\"app/auth.ts\"\n     * async authorized({ request, auth }) {\n     *   const url = request.nextUrl\n     *\n     *   if(request.method === \"POST\") {\n     *     const { authToken } = (await request.json()) ?? {}\n     *     // If the request has a valid auth token, it is authorized\n     *     const valid = await validateAuthToken(authToken)\n     *     if(valid) return true\n     *     return NextResponse.json(\"Invalid auth token\", { status: 401 })\n     *   }\n     *\n     *   // Logged in users are authenticated, otherwise redirect to login page\n     *   return !!auth.user\n     * }\n     * ```\n     *\n     * :::warning\n     * If you are returning a redirect response, make sure that the page you are redirecting to is not protected by this callback,\n     * otherwise you could end up in an infinite redirect loop.\n     * :::\n     */\n    authorized?: (params: {\n      /** The request to be authorized. */\n      request: NextRequest\n      /** The authenticated user or token, if any. */\n      auth: Session | null\n    }) => Awaitable<boolean | NextResponse | Response | undefined>\n  }\n}\n\nasync function getSession(headers: Headers, config: NextAuthConfig) {\n  const url = createActionURL(\n    \"session\",\n    // @ts-expect-error `x-forwarded-proto` is not nullable, next.js sets it by default\n    headers.get(\"x-forwarded-proto\"),\n    headers,\n    process.env,\n    config\n  )\n  const request = new Request(url, {\n    headers: { cookie: headers.get(\"cookie\") ?? \"\" },\n  })\n\n  return Auth(request, {\n    ...config,\n    callbacks: {\n      ...config.callbacks,\n      // Since we are server-side, we don't need to filter out the session data\n      // See https://authjs.dev/getting-started/migrating-to-v5#authenticating-server-side\n      // TODO: Taint the session data to prevent accidental leakage to the client\n      // https://react.dev/reference/react/experimental_taintObjectReference\n      async session(...args) {\n        const session =\n          // If the user defined a custom session callback, use that instead\n          (await config.callbacks?.session?.(...args)) ?? {\n            ...args[0].session,\n            expires:\n              args[0].session.expires?.toISOString?.() ??\n              args[0].session.expires,\n          }\n        const user = args[0].user ?? args[0].token\n        return { user, ...session } satisfies Session\n      },\n    },\n  }) as Promise<Response>\n}\n\nexport interface NextAuthRequest extends NextRequest {\n  auth: Session | null\n}\n\nexport type NextAuthMiddleware = (\n  request: NextAuthRequest,\n  event: NextFetchEvent\n) => ReturnType<NextMiddleware>\n\nexport type WithAuthArgs =\n  | [NextAuthRequest, any]\n  | [NextAuthMiddleware]\n  | [AppRouteHandlerFn]\n  | [NextApiRequest, NextApiResponse]\n  | [GetServerSidePropsContext]\n  | []\n\nfunction isReqWrapper(arg: any): arg is NextAuthMiddleware | AppRouteHandlerFn {\n  return typeof arg === \"function\"\n}\n\nexport function initAuth(\n  config:\n    | NextAuthConfig\n    | ((request: NextRequest | undefined) => Awaitable<NextAuthConfig>),\n  onLazyLoad?: (config: NextAuthConfig) => void // To set the default env vars\n) {\n  if (typeof config === \"function\") {\n    return async (...args: WithAuthArgs) => {\n      if (!args.length) {\n        // React Server Components\n        const _headers = await headers()\n        const _config = await config(undefined) // Review: Should we pass headers() here instead?\n        onLazyLoad?.(_config)\n\n        return getSession(_headers, _config).then((r) => r.json())\n      }\n\n      if (args[0] instanceof Request) {\n        // middleware.ts inline\n        // export { auth as default } from \"auth\"\n        const req = args[0]\n        const ev = args[1]\n        const _config = await config(req)\n        onLazyLoad?.(_config)\n\n        // args[0] is supposed to be NextRequest but the instanceof check is failing.\n        return handleAuth([req, ev], _config)\n      }\n\n      if (isReqWrapper(args[0])) {\n        // middleware.ts wrapper/route.ts\n        // import { auth } from \"auth\"\n        // export default auth((req) => { console.log(req.auth) }})\n        const userMiddlewareOrRoute = args[0]\n        return async (\n          ...args: Parameters<NextAuthMiddleware | AppRouteHandlerFn>\n        ) => {\n          const _config = await config(args[0])\n          onLazyLoad?.(_config)\n          return handleAuth(args, _config, userMiddlewareOrRoute)\n        }\n      }\n      // API Routes, getServerSideProps\n      const request = \"req\" in args[0] ? args[0].req : args[0]\n      const response: any = \"res\" in args[0] ? args[0].res : args[1]\n      const _config = await config(request)\n      onLazyLoad?.(_config)\n\n      // @ts-expect-error -- request is NextRequest\n      return getSession(new Headers(request.headers), _config).then(\n        async (authResponse) => {\n          const auth = await authResponse.json()\n\n          for (const cookie of authResponse.headers.getSetCookie())\n            if (\"headers\" in response)\n              response.headers.append(\"set-cookie\", cookie)\n            else response.appendHeader(\"set-cookie\", cookie)\n\n          return auth satisfies Session | null\n        }\n      )\n    }\n  }\n  return (...args: WithAuthArgs) => {\n    if (!args.length) {\n      // React Server Components\n      return Promise.resolve(headers()).then((h: Headers) =>\n        getSession(h, config).then((r) => r.json())\n      )\n    }\n    if (args[0] instanceof Request) {\n      // middleware.ts inline\n      // export { auth as default } from \"auth\"\n      const req = args[0]\n      const ev = args[1]\n      return handleAuth([req, ev], config)\n    }\n\n    if (isReqWrapper(args[0])) {\n      // middleware.ts wrapper/route.ts\n      // import { auth } from \"auth\"\n      // export default auth((req) => { console.log(req.auth) }})\n      const userMiddlewareOrRoute = args[0]\n      return async (\n        ...args: Parameters<NextAuthMiddleware | AppRouteHandlerFn>\n      ) => {\n        return handleAuth(args, config, userMiddlewareOrRoute).then((res) => {\n          return res\n        })\n      }\n    }\n\n    // API Routes, getServerSideProps\n    const request = \"req\" in args[0] ? args[0].req : args[0]\n    const response: any = \"res\" in args[0] ? args[0].res : args[1]\n\n    return getSession(\n      // @ts-expect-error\n      new Headers(request.headers),\n      config\n    ).then(async (authResponse) => {\n      const auth = await authResponse.json()\n\n      for (const cookie of authResponse.headers.getSetCookie())\n        if (\"headers\" in response) response.headers.append(\"set-cookie\", cookie)\n        else response.appendHeader(\"set-cookie\", cookie)\n\n      return auth satisfies Session | null\n    })\n  }\n}\n\nasync function handleAuth(\n  args: Parameters<NextMiddleware | AppRouteHandlerFn>,\n  config: NextAuthConfig,\n  userMiddlewareOrRoute?: NextAuthMiddleware | AppRouteHandlerFn\n) {\n  const request = reqWithEnvURL(args[0])\n  const sessionResponse = await getSession(request.headers, config)\n  const auth = await sessionResponse.json()\n\n  let authorized: boolean | NextResponse | Response | undefined = true\n\n  if (config.callbacks?.authorized) {\n    authorized = await config.callbacks.authorized({ request, auth })\n  }\n\n  let response: any = NextResponse.next?.()\n\n  if (authorized instanceof Response) {\n    // User returned a custom response, like redirecting to a page or 401, respect it\n    response = authorized\n\n    const redirect = authorized.headers.get(\"Location\")\n    const { pathname } = request.nextUrl\n    // If the user is redirecting to the same NextAuth.js action path as the current request,\n    // don't allow the redirect to prevent an infinite loop\n    if (\n      redirect &&\n      isSameAuthAction(pathname, new URL(redirect).pathname, config)\n    ) {\n      authorized = true\n    }\n  } else if (userMiddlewareOrRoute) {\n    // Execute user's middleware/handler with the augmented request\n    const augmentedReq = request as NextAuthRequest\n    augmentedReq.auth = auth\n    response =\n      (await userMiddlewareOrRoute(augmentedReq, args[1])) ??\n      NextResponse.next()\n  } else if (!authorized) {\n    const signInPage = config.pages?.signIn ?? `${config.basePath}/signin`\n    if (request.nextUrl.pathname !== signInPage) {\n      // Redirect to signin page by default if not authorized\n      const signInUrl = request.nextUrl.clone()\n      signInUrl.pathname = signInPage\n      signInUrl.searchParams.set(\"callbackUrl\", request.nextUrl.href)\n      response = NextResponse.redirect(signInUrl)\n    }\n  }\n\n  const finalResponse = new Response(response?.body, response)\n\n  // Preserve cookies from the session response\n  for (const cookie of sessionResponse.headers.getSetCookie())\n    finalResponse.headers.append(\"set-cookie\", cookie)\n\n  return finalResponse\n}\n\nfunction isSameAuthAction(\n  requestPath: string,\n  redirectPath: string,\n  config: NextAuthConfig\n) {\n  const action = redirectPath.replace(`${requestPath}/`, \"\") as AuthAction\n  const pages = Object.values(config.pages ?? {})\n\n  return (\n    (actions.has(action) || pages.includes(redirectPath)) &&\n    redirectPath === requestPath\n  )\n}\n\nconst actions = new Set<AuthAction>([\n  \"providers\",\n  \"session\",\n  \"csrf\",\n  \"signin\",\n  \"signout\",\n  \"callback\",\n  \"verify-request\",\n  \"error\",\n])\n"
  },
  {
    "path": "packages/next-auth/src/lib/types.ts",
    "content": "// @ts-expect-error Next.js does not yet correctly use the `package.json#exports` field\nimport type { NextRequest } from \"next/server\"\n\n/**\n * AppRouteHandlerFnContext is the context that is passed to the handler as the\n * second argument.\n */\nexport type AppRouteHandlerFnContext = {\n  params: Promise<any>\n}\n/**\n * Handler function for app routes. If a non-Response value is returned, an error\n * will be thrown.\n */\nexport type AppRouteHandlerFn = (\n  /**\n   * Incoming request object.\n   */\n  req: NextRequest,\n  /**\n   * Context properties on the request (including the parameters if this was a\n   * dynamic route).\n   */\n  ctx: AppRouteHandlerFnContext\n) => void | Response | Promise<void | Response>\n\nexport type AppRouteHandlers = Record<\n  \"GET\" | \"POST\",\n  (req: NextRequest) => Promise<Response>\n>\n"
  },
  {
    "path": "packages/next-auth/src/middleware.ts",
    "content": "/**\n * :::warning Deprecated\n * This module is replaced in v5. Read more at: https://authjs.dev/getting-started/migrating-to-v5#authenticating-server-side\n * :::\n *\n * @module middleware\n */\n\nthrow new ReferenceError(\n  [\n    '\"next-auth/middleware\" is deprecated. If you are not ready to migrate, keep using \"next-auth@4\".',\n    \"Read more on https://authjs.dev/getting-started/migrating-to-v5\",\n  ].join(\"\\n\")\n)\n\nexport {}\n"
  },
  {
    "path": "packages/next-auth/src/next.ts",
    "content": "/**\n * :::warning Deprecated\n * This module is replaced in v5. Read more at: https://authjs.dev/getting-started/migrating-to-v5#authenticating-server-side\n * :::\n *\n * @module next\n */\n\nthrow new ReferenceError(\n  [\n    '\"next-auth/next\" is deprecated. If you are not ready to migrate, keep using \"next-auth@4\".',\n    \"Read more on https://authjs.dev/getting-started/migrating-to-v5\",\n  ].join(\"\\n\")\n)\n\nexport {}\n"
  },
  {
    "path": "packages/next-auth/src/react.tsx",
    "content": "/**\n *\n * NextAuth.js is the official integration of Auth.js for Next.js applications. It supports both\n * [Client Components](https://nextjs.org/docs/app/building-your-application/rendering/client-components) and the\n * [Pages Router](https://nextjs.org/docs/pages). It includes methods for signing in, signing out, hooks, and a React\n * Context provider to wrap your application and make session data available anywhere.\n *\n * For use in [Server Actions](https://nextjs.org/docs/app/api-reference/functions/server-actions), check out [these methods](https://authjs.dev/guides/upgrade-to-v5#methods)\n *\n * @module react\n */\n\n\"use client\"\n\nimport * as React from \"react\"\nimport {\n  apiBaseUrl,\n  ClientSessionError,\n  fetchData,\n  now,\n  parseUrl,\n  useOnline,\n} from \"./lib/client.js\"\n\nimport type { ProviderId } from \"@auth/core/providers\"\nimport type { LoggerInstance, Session } from \"@auth/core/types\"\nimport type {\n  AuthClientConfig,\n  ClientSafeProvider,\n  SessionProviderProps,\n  SignInAuthorizationParams,\n  SignInOptions,\n  SignInResponse,\n  SignOutParams,\n  SignOutResponse,\n  UseSessionOptions,\n} from \"./lib/client.js\"\n\n// TODO: Remove/move to core?\nexport type {\n  SignInOptions,\n  SignInAuthorizationParams,\n  SignOutParams,\n  SignInResponse,\n}\n\nexport { SessionProviderProps }\n// This behaviour mirrors the default behaviour for getting the site name that\n// happens server side in server/index.js\n// 1. An empty value is legitimate when the code is being invoked client side as\n//    relative URLs are valid in that context and so defaults to empty.\n// 2. When invoked server side the value is picked up from an environment\n//    variable and defaults to 'http://localhost:3000'.\nexport const __NEXTAUTH: AuthClientConfig = {\n  baseUrl: parseUrl(process.env.NEXTAUTH_URL ?? process.env.VERCEL_URL).origin,\n  basePath: parseUrl(process.env.NEXTAUTH_URL).path,\n  baseUrlServer: parseUrl(\n    process.env.NEXTAUTH_URL_INTERNAL ??\n      process.env.NEXTAUTH_URL ??\n      process.env.VERCEL_URL\n  ).origin,\n  basePathServer: parseUrl(\n    process.env.NEXTAUTH_URL_INTERNAL ?? process.env.NEXTAUTH_URL\n  ).path,\n  _lastSync: 0,\n  _session: undefined,\n  _getSession: () => {},\n}\n\n// https://github.com/nextauthjs/next-auth/pull/10762\nlet broadcastChannel: BroadcastChannel | null = null\n\nfunction getNewBroadcastChannel() {\n  if (typeof BroadcastChannel === \"undefined\") {\n    return {\n      postMessage: () => {},\n      addEventListener: () => {},\n      removeEventListener: () => {},\n      name: \"next-auth\",\n      onmessage: null,\n      onmessageerror: null,\n      close: () => {},\n      dispatchEvent: () => false,\n    } satisfies BroadcastChannel\n  }\n\n  return new BroadcastChannel(\"next-auth\")\n}\n\nfunction broadcast() {\n  if (broadcastChannel === null) {\n    broadcastChannel = getNewBroadcastChannel()\n  }\n\n  return broadcastChannel\n}\n\n// TODO:\nconst logger: LoggerInstance = {\n  debug: console.debug,\n  error: console.error,\n  warn: console.warn,\n}\n\n/** @todo Document */\nexport type UpdateSession = (data?: any) => Promise<Session | null>\n\n/**\n * useSession() returns an object containing three things: a method called {@link UpdateSession|update}, `data` and `status`.\n */\nexport type SessionContextValue<R extends boolean = false> = R extends true\n  ?\n      | { update: UpdateSession; data: Session; status: \"authenticated\" }\n      | { update: UpdateSession; data: null; status: \"loading\" }\n  :\n      | { update: UpdateSession; data: Session; status: \"authenticated\" }\n      | {\n          update: UpdateSession\n          data: null\n          status: \"unauthenticated\" | \"loading\"\n        }\n\nexport const SessionContext = React.createContext?.<\n  SessionContextValue | undefined\n>(undefined)\n\n/**\n * React Hook that gives you access to the logged in user's session data and lets you modify it.\n *\n * :::info\n * `useSession` is for client-side use only and when using [Next.js App Router (`app/`)](https://nextjs.org/blog/next-13-4#nextjs-app-router) you should prefer the `auth()` export.\n * :::\n */\nexport function useSession<R extends boolean>(\n  options?: UseSessionOptions<R>\n): SessionContextValue<R> {\n  if (!SessionContext) {\n    throw new Error(\"React Context is unavailable in Server Components\")\n  }\n\n  // @ts-expect-error Satisfy TS if branch on line below\n  const value: SessionContextValue<R> = React.useContext(SessionContext)\n  if (!value && process.env.NODE_ENV !== \"production\") {\n    throw new Error(\n      \"[next-auth]: `useSession` must be wrapped in a <SessionProvider />\"\n    )\n  }\n\n  const { required, onUnauthenticated } = options ?? {}\n\n  const requiredAndNotLoading = required && value.status === \"unauthenticated\"\n\n  React.useEffect(() => {\n    if (requiredAndNotLoading) {\n      const url = `${__NEXTAUTH.basePath}/signin?${new URLSearchParams({\n        error: \"SessionRequired\",\n        callbackUrl: window.location.href,\n      })}`\n      if (onUnauthenticated) onUnauthenticated()\n      else window.location.href = url\n    }\n  }, [requiredAndNotLoading, onUnauthenticated])\n\n  if (requiredAndNotLoading) {\n    return {\n      data: value.data,\n      update: value.update,\n      status: \"loading\",\n    }\n  }\n\n  return value\n}\n\nexport interface GetSessionParams {\n  event?: \"storage\" | \"timer\" | \"hidden\" | string\n  triggerEvent?: boolean\n  broadcast?: boolean\n}\n\nexport async function getSession(params?: GetSessionParams) {\n  const session = await fetchData<Session>(\n    \"session\",\n    __NEXTAUTH,\n    logger,\n    params\n  )\n  if (params?.broadcast ?? true) {\n    // https://github.com/nextauthjs/next-auth/pull/11470\n    getNewBroadcastChannel().postMessage({\n      event: \"session\",\n      data: { trigger: \"getSession\" },\n    })\n  }\n  return session\n}\n\n/**\n * Returns the current Cross-Site Request Forgery Token (CSRF Token)\n * required to make requests that changes state. (e.g. signing in or out, or updating the session).\n *\n * [CSRF Prevention: Double Submit Cookie](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie)\n */\nexport async function getCsrfToken() {\n  const response = await fetchData<{ csrfToken: string }>(\n    \"csrf\",\n    __NEXTAUTH,\n    logger\n  )\n  return response?.csrfToken ?? \"\"\n}\n\nexport async function getProviders() {\n  return fetchData<Record<ProviderId, ClientSafeProvider>>(\n    \"providers\",\n    __NEXTAUTH,\n    logger\n  )\n}\n\n/**\n * Initiates a signin flow or sends the user to the signin page listing all possible providers.\n * Handles CSRF protection.\n *\n * @note This method can only be used from Client Components (\"use client\" or Pages Router).\n * For Server Actions, use the `signIn` method imported from the `auth` config.\n */\nexport async function signIn(\n  provider?: ProviderId,\n  options?: SignInOptions<true>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<void>\nexport async function signIn(\n  provider?: ProviderId,\n  options?: SignInOptions<false>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<SignInResponse>\nexport async function signIn<Redirect extends boolean = true>(\n  provider?: ProviderId,\n  options?: SignInOptions<Redirect>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<SignInResponse | void> {\n  const { callbackUrl, ...rest } = options ?? {}\n  const {\n    redirect = true,\n    redirectTo = callbackUrl ?? window.location.href,\n    ...signInParams\n  } = rest\n\n  const baseUrl = apiBaseUrl(__NEXTAUTH)\n  const providers = await getProviders()\n\n  if (!providers) {\n    const url = `${baseUrl}/error`\n    window.location.href = url\n    return // TODO: Return error if `redirect: false`\n  }\n\n  if (!provider || !providers[provider]) {\n    const url = `${baseUrl}/signin?${new URLSearchParams({\n      callbackUrl: redirectTo,\n    })}`\n    window.location.href = url\n    return // TODO: Return error if `redirect: false`\n  }\n\n  const providerType = providers[provider].type\n\n  if (providerType === \"webauthn\") {\n    // TODO: Add docs link with explanation\n    throw new TypeError(\n      [\n        `Provider id \"${provider}\" refers to a WebAuthn provider.`,\n        'Please use `import { signIn } from \"next-auth/webauthn\"` instead.',\n      ].join(\"\\n\")\n    )\n  }\n\n  const signInUrl = `${baseUrl}/${\n    providerType === \"credentials\" ? \"callback\" : \"signin\"\n  }/${provider}`\n\n  const csrfToken = await getCsrfToken()\n  const res = await fetch(\n    `${signInUrl}?${new URLSearchParams(authorizationParams)}`,\n    {\n      method: \"post\",\n      headers: {\n        \"Content-Type\": \"application/x-www-form-urlencoded\",\n        \"X-Auth-Return-Redirect\": \"1\",\n      },\n      body: new URLSearchParams({\n        ...signInParams,\n        csrfToken,\n        callbackUrl: redirectTo,\n      }),\n    }\n  )\n\n  const data = await res.json()\n\n  if (redirect) {\n    const url = data.url ?? redirectTo\n    window.location.href = url\n    // If url contains a hash, the browser does not reload the page. We reload manually\n    if (url.includes(\"#\")) window.location.reload()\n    return\n  }\n\n  const error = new URL(data.url).searchParams.get(\"error\") ?? undefined\n  const code = new URL(data.url).searchParams.get(\"code\") ?? undefined\n\n  if (res.ok) {\n    await __NEXTAUTH._getSession({ event: \"storage\" })\n  }\n\n  return {\n    error,\n    code,\n    status: res.status,\n    ok: res.ok,\n    url: error ? null : data.url,\n  }\n}\n\n/**\n * Initiate a signout, by destroying the current session.\n * Handles CSRF protection.\n *\n * @note This method can only be used from Client Components (\"use client\" or Pages Router).\n * For Server Actions, use the `signOut` method imported from the `auth` config.\n */\nexport async function signOut(options?: SignOutParams<true>): Promise<void>\nexport async function signOut(\n  options?: SignOutParams<false>\n): Promise<SignOutResponse>\nexport async function signOut<R extends boolean = true>(\n  options?: SignOutParams<R>\n): Promise<SignOutResponse | void> {\n  const {\n    redirect = true,\n    redirectTo = options?.callbackUrl ?? window.location.href,\n  } = options ?? {}\n\n  const baseUrl = apiBaseUrl(__NEXTAUTH)\n  const csrfToken = await getCsrfToken()\n  const res = await fetch(`${baseUrl}/signout`, {\n    method: \"post\",\n    headers: {\n      \"Content-Type\": \"application/x-www-form-urlencoded\",\n      \"X-Auth-Return-Redirect\": \"1\",\n    },\n    body: new URLSearchParams({ csrfToken, callbackUrl: redirectTo }),\n  })\n  const data = await res.json()\n\n  broadcast().postMessage({ event: \"session\", data: { trigger: \"signout\" } })\n\n  if (redirect) {\n    const url = data.url ?? redirectTo\n    window.location.href = url\n    // If url contains a hash, the browser does not reload the page. We reload manually\n    if (url.includes(\"#\")) window.location.reload()\n    return\n  }\n\n  await __NEXTAUTH._getSession({ event: \"storage\" })\n\n  return data\n}\n\n/**\n * [React Context](https://react.dev/learn/passing-data-deeply-with-context) provider to wrap the app (`pages/`) to make session data available anywhere.\n *\n * When used, the session state is automatically synchronized across all open tabs/windows and they are all updated whenever they gain or lose focus\n * or the state changes (e.g. a user signs in or out) when {@link SessionProviderProps.refetchOnWindowFocus} is `true`.\n *\n * :::info\n * `SessionProvider` is for client-side use only and when using [Next.js App Router (`app/`)](https://nextjs.org/blog/next-13-4#nextjs-app-router) you should prefer the `auth()` export.\n * :::\n */\nexport function SessionProvider(props: SessionProviderProps) {\n  if (!SessionContext) {\n    throw new Error(\"React Context is unavailable in Server Components\")\n  }\n\n  const { children, basePath, refetchInterval, refetchWhenOffline } = props\n\n  if (basePath) __NEXTAUTH.basePath = basePath\n\n  /**\n   * If session was `null`, there was an attempt to fetch it,\n   * but it failed, but we still treat it as a valid initial value.\n   */\n  const hasInitialSession = props.session !== undefined\n\n  /** If session was passed, initialize as already synced */\n  __NEXTAUTH._lastSync = hasInitialSession ? now() : 0\n\n  const [session, setSession] = React.useState(() => {\n    if (hasInitialSession) __NEXTAUTH._session = props.session\n    return props.session\n  })\n\n  /** If session was passed, initialize as not loading */\n  const [loading, setLoading] = React.useState(!hasInitialSession)\n\n  React.useEffect(() => {\n    __NEXTAUTH._getSession = async ({ event } = {}) => {\n      try {\n        const storageEvent = event === \"storage\"\n        // We should always update if we don't have a client session yet\n        // or if there are events from other tabs/windows\n        if (storageEvent || __NEXTAUTH._session === undefined) {\n          __NEXTAUTH._lastSync = now()\n          __NEXTAUTH._session = await getSession({\n            broadcast: !storageEvent,\n          })\n          setSession(__NEXTAUTH._session)\n          return\n        }\n\n        if (\n          // If there is no time defined for when a session should be considered\n          // stale, then it's okay to use the value we have until an event is\n          // triggered which updates it\n          !event ||\n          // If the client doesn't have a session then we don't need to call\n          // the server to check if it does (if they have signed in via another\n          // tab or window that will come through as a \"stroage\" event\n          // event anyway)\n          __NEXTAUTH._session === null ||\n          // Bail out early if the client session is not stale yet\n          now() < __NEXTAUTH._lastSync\n        ) {\n          return\n        }\n\n        // An event or session staleness occurred, update the client session.\n        __NEXTAUTH._lastSync = now()\n        __NEXTAUTH._session = await getSession()\n        setSession(__NEXTAUTH._session)\n      } catch (error) {\n        logger.error(\n          new ClientSessionError((error as Error).message, error as any)\n        )\n      } finally {\n        setLoading(false)\n      }\n    }\n\n    __NEXTAUTH._getSession()\n\n    return () => {\n      __NEXTAUTH._lastSync = 0\n      __NEXTAUTH._session = undefined\n      __NEXTAUTH._getSession = () => {}\n    }\n  }, [])\n\n  React.useEffect(() => {\n    const handle = () => __NEXTAUTH._getSession({ event: \"storage\" })\n    // Listen for storage events and update session if event fired from\n    // another window (but suppress firing another event to avoid a loop)\n    // Fetch new session data but tell it to not to fire another event to\n    // avoid an infinite loop.\n    // Note: We could pass session data through and do something like\n    // `setData(message.data)` but that can cause problems depending\n    // on how the session object is being used in the client; it is\n    // more robust to have each window/tab fetch it's own copy of the\n    // session object rather than share it across instances.\n    broadcast().addEventListener(\"message\", handle)\n    return () => broadcast().removeEventListener(\"message\", handle)\n  }, [])\n\n  React.useEffect(() => {\n    const { refetchOnWindowFocus = true } = props\n    // Listen for when the page is visible, if the user switches tabs\n    // and makes our tab visible again, re-fetch the session, but only if\n    // this feature is not disabled.\n    const visibilityHandler = () => {\n      if (refetchOnWindowFocus && document.visibilityState === \"visible\")\n        __NEXTAUTH._getSession({ event: \"visibilitychange\" })\n    }\n    document.addEventListener(\"visibilitychange\", visibilityHandler, false)\n    return () =>\n      document.removeEventListener(\"visibilitychange\", visibilityHandler, false)\n  }, [props.refetchOnWindowFocus])\n\n  const isOnline = useOnline()\n  // TODO: Flip this behavior in next major version\n  const shouldRefetch = refetchWhenOffline !== false || isOnline\n\n  React.useEffect(() => {\n    if (refetchInterval && shouldRefetch) {\n      const refetchIntervalTimer = setInterval(() => {\n        if (__NEXTAUTH._session) {\n          __NEXTAUTH._getSession({ event: \"poll\" })\n        }\n      }, refetchInterval * 1000)\n      return () => clearInterval(refetchIntervalTimer)\n    }\n  }, [refetchInterval, shouldRefetch])\n\n  const value: any = React.useMemo(\n    () => ({\n      data: session,\n      status: loading\n        ? \"loading\"\n        : session\n          ? \"authenticated\"\n          : \"unauthenticated\",\n      async update(data: any) {\n        if (loading) return\n        setLoading(true)\n        const newSession = await fetchData<Session>(\n          \"session\",\n          __NEXTAUTH,\n          logger,\n          typeof data === \"undefined\"\n            ? undefined\n            : { body: { csrfToken: await getCsrfToken(), data } }\n        )\n        setLoading(false)\n        if (newSession) {\n          setSession(newSession)\n          broadcast().postMessage({\n            event: \"session\",\n            data: { trigger: \"getSession\" },\n          })\n        }\n        return newSession\n      },\n    }),\n    [session, loading]\n  )\n\n  return (\n    // @ts-expect-error\n    <SessionContext.Provider value={value}>{children}</SessionContext.Provider>\n  )\n}\n"
  },
  {
    "path": "packages/next-auth/src/webauthn.ts",
    "content": "import { apiBaseUrl } from \"./lib/client.js\"\nimport { startAuthentication, startRegistration } from \"@simplewebauthn/browser\"\nimport { getCsrfToken, getProviders, __NEXTAUTH } from \"./react.js\"\n\nimport type { LoggerInstance } from \"@auth/core/types\"\nimport type { WebAuthnOptionsResponseBody } from \"@auth/core/types\"\nimport type { ProviderId } from \"@auth/core/providers\"\nimport type {\n  AuthClientConfig,\n  SignInAuthorizationParams,\n  SignInOptions,\n  SignInResponse,\n} from \"./lib/client.js\"\n\nconst logger: LoggerInstance = {\n  debug: console.debug,\n  error: console.error,\n  warn: console.warn,\n}\n\n/**\n * Fetch webauthn options from server and prompt user for authentication or registration.\n * Returns either the completed WebAuthn response or an error request.\n */\nasync function webAuthnOptions(\n  providerID: ProviderId,\n  nextAuthConfig: AuthClientConfig,\n  options?: Omit<SignInOptions, \"redirect\">\n) {\n  const baseUrl = apiBaseUrl(nextAuthConfig)\n\n  // @ts-expect-error\n  const params = new URLSearchParams(options)\n\n  const optionsResp = await fetch(\n    `${baseUrl}/webauthn-options/${providerID}?${params}`\n  )\n  if (!optionsResp.ok) {\n    return { error: optionsResp }\n  }\n  const optionsData: WebAuthnOptionsResponseBody = await optionsResp.json()\n\n  if (optionsData.action === \"authenticate\") {\n    const webAuthnResponse = await startAuthentication(optionsData.options)\n    return { data: webAuthnResponse, action: \"authenticate\" }\n  } else {\n    const webAuthnResponse = await startRegistration(optionsData.options)\n    return { data: webAuthnResponse, action: \"register\" }\n  }\n}\n\n/**\n * Initiate a WebAuthn signin flow.\n * @see https://authjs.dev/getting-started/authentication/webauthn\n */\nexport async function signIn(\n  provider?: ProviderId,\n  options?: SignInOptions<true>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<void>\nexport async function signIn(\n  provider?: ProviderId,\n  options?: SignInOptions<false>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<SignInResponse>\nexport async function signIn<Redirect extends boolean = true>(\n  provider?: ProviderId,\n  options?: SignInOptions<Redirect>,\n  authorizationParams?: SignInAuthorizationParams\n): Promise<SignInResponse | void> {\n  const { callbackUrl, ...rest } = options ?? {}\n  const {\n    redirectTo = callbackUrl ?? window.location.href,\n    redirect = true,\n    ...signInParams\n  } = rest\n\n  const baseUrl = apiBaseUrl(__NEXTAUTH)\n  const providers = await getProviders()\n\n  if (!providers) {\n    window.location.href = `${baseUrl}/error`\n    return // TODO: Return error if `redirect: false`\n  }\n\n  if (\n    !provider ||\n    !providers[provider] ||\n    providers[provider].type !== \"webauthn\"\n  ) {\n    // TODO: Add docs link with explanation\n    throw new TypeError(\n      [\n        `Provider id \"${provider}\" does not refer to a WebAuthn provider.`,\n        'Please use `import { signIn } from \"next-auth/react\"` instead.',\n      ].join(\"\\n\")\n    )\n  }\n\n  const webAuthnBody: Record<string, unknown> = {}\n  const webAuthnResponse = await webAuthnOptions(\n    provider,\n    __NEXTAUTH,\n    signInParams\n  )\n  if (webAuthnResponse.error) {\n    logger.error(new Error(await webAuthnResponse.error.text()))\n    return\n  }\n  webAuthnBody.data = JSON.stringify(webAuthnResponse.data)\n  webAuthnBody.action = webAuthnResponse.action\n\n  const signInUrl = `${baseUrl}/callback/${provider}?${new URLSearchParams(authorizationParams)}`\n  const res = await fetch(signInUrl, {\n    method: \"post\",\n    headers: {\n      \"Content-Type\": \"application/x-www-form-urlencoded\",\n      \"X-Auth-Return-Redirect\": \"1\",\n    },\n    body: new URLSearchParams({\n      ...signInParams,\n      ...webAuthnBody,\n      csrfToken: await getCsrfToken(),\n      callbackUrl: redirectTo,\n    }),\n  })\n\n  const data = await res.json()\n\n  if (redirect) {\n    const url = data.url ?? callbackUrl\n    window.location.href = url\n    // If url contains a hash, the browser does not reload the page. We reload manually\n    if (url.includes(\"#\")) window.location.reload()\n    return\n  }\n\n  const error = new URL(data.url).searchParams.get(\"error\")\n  const code = new URL(data.url).searchParams.get(\"code\")\n\n  if (res.ok) {\n    await __NEXTAUTH._getSession({ event: \"storage\" })\n  }\n\n  return {\n    error,\n    code,\n    status: res.status,\n    ok: res.ok,\n    url: error ? null : data.url,\n  } as any\n}\n"
  },
  {
    "path": "packages/next-auth/test/actions.test.ts",
    "content": "import { describe, it, expect, vi, beforeEach, afterEach } from \"vitest\"\nimport NextAuth, { NextAuthConfig } from \"../src\"\n// TODO: Move the MemoryAdapter to utils package\nimport { MemoryAdapter } from \"../../core/test/memory-adapter\"\nimport Nodemailer from \"@auth/core/providers/nodemailer\"\n\nlet mockedHeaders = vi.hoisted(() => {\n  return new globalThis.Headers()\n})\n\nconst mockRedirect = vi.hoisted(() => vi.fn())\n\nvi.mock(\"next/navigation\", async (importOriginal) => {\n  const originalModule = await importOriginal()\n  return {\n    // @ts-expect-error - not typed\n    ...originalModule,\n    redirect: mockRedirect,\n  }\n})\n\nvi.mock(\"next/headers\", async (importOriginal) => {\n  const originalModule = await importOriginal()\n  return {\n    // @ts-expect-error - not typed\n    ...originalModule,\n    headers: () => mockedHeaders,\n    cookies: () => {\n      const cookies: { [key: string]: unknown } = {}\n      return {\n        get: (name: string) => {\n          return cookies[name]\n        },\n        set: (name: string, value: string) => {\n          cookies[name] = value\n        },\n      }\n    },\n  }\n})\n\nconst options = {\n  email: \"jane@example.com\",\n} satisfies Parameters<ReturnType<typeof NextAuth>[\"signIn\"]>[1]\n\nlet nextAuth: ReturnType<typeof NextAuth> | null = null\n\nlet config: NextAuthConfig = {\n  adapter: MemoryAdapter(),\n  providers: [\n    Nodemailer({\n      sendVerificationRequest() {\n        // ignore\n      },\n      server: {\n        host: \"smtp.example.com\",\n        port: 465,\n        secure: true,\n      },\n    }),\n  ],\n}\n\ndescribe(\"signIn action\", () => {\n  beforeEach(() => {\n    process.env.AUTH_SECRET = \"secret\"\n    process.env.AUTH_URL = \"http://localhost\"\n    nextAuth = NextAuth(config)\n  })\n  afterEach(() => {\n    process.env.AUTH_SECRET = \"\"\n    process.env.AUTH_URL = \"\"\n    nextAuth = null\n    vi.resetAllMocks()\n  })\n  describe(\"with Nodemailer provider\", () => {\n    it(\"redirects to /verify-request\", async () => {\n      await nextAuth?.signIn(\"nodemailer\", options)\n      expect(mockRedirect).toHaveBeenCalledWith(\n        \"http://localhost/api/auth/verify-request?provider=nodemailer&type=email\"\n      )\n    })\n\n    it(\"redirects to /error page when sendVerificationRequest throws\", async () => {\n      nextAuth = NextAuth({\n        ...config,\n        providers: [\n          Nodemailer({\n            sendVerificationRequest() {\n              throw new Error()\n            },\n            server: {\n              host: \"smtp.example.com\",\n              port: 465,\n              secure: true,\n            },\n          }),\n        ],\n      })\n      const redirectTo = await nextAuth.signIn(\"nodemailer\", {\n        ...options,\n        redirect: false,\n      })\n      expect(redirectTo).toEqual(\n        \"http://localhost/api/auth/error?error=Configuration\"\n      )\n    })\n  })\n})\n"
  },
  {
    "path": "packages/next-auth/test/e2e/fixtures/auth.ts",
    "content": "export type AuthFixture = {\n  environmentUrl: string\n  loginUser: string\n  loginPassword: string\n}\nexport function createAuthFixture(): AuthFixture {\n  return {\n    get environmentUrl() {\n      const url = process.env.ENVIRONMENT_URL ?? \"http://localhost:3000\"\n\n      return url\n    },\n    get loginUser() {\n      const username = process.env.TEST_KEYCLOAK_USERNAME\n      if (!username) throw new Error(\"Keycloak username is empty\")\n\n      return username\n    },\n    get loginPassword() {\n      const password = process.env.TEST_KEYCLOAK_PASSWORD\n      if (!password) throw new Error(\"Keycloak password is empty\")\n\n      return password\n    },\n  }\n}\n"
  },
  {
    "path": "packages/next-auth/test/e2e/fixtures/webApp.ts",
    "content": "import {\n  expect,\n  type BrowserContext,\n  type Locator,\n  type Page,\n} from \"@playwright/test\"\nimport { AuthFixture } from \"./auth\"\nimport { KeycloakLoginPom } from \"../poms/keycloakLoginPom\"\n\n/**\n * This fixture provides utility methods for logging in,\n * navigating and clicking common elements.\n */\nexport class WebApp {\n  #auth: AuthFixture\n  private keycloak: KeycloakLoginPom\n\n  public page: Page\n  public context: BrowserContext\n\n  public isLoggedIn = false\n\n  // locators\n  public signinButton: Locator\n  public session: Record<string, any>\n\n  constructor({\n    page,\n    context,\n    auth,\n    keycloak,\n  }: {\n    page: Page\n    context: BrowserContext\n    auth: AuthFixture\n    keycloak: KeycloakLoginPom\n  }) {\n    this.#auth = auth\n\n    this.keycloak = keycloak\n    this.page = page\n    this.context = context\n\n    this.signinButton = page\n      .getByRole(\"banner\")\n      .getByRole(\"button\", { name: \"Sign in\" })\n\n    this.session = {}\n  }\n\n  async login({\n    environmentUrl = this.#auth.environmentUrl,\n    username,\n    password,\n  }: {\n    environmentUrl?: string\n    username?: string\n    password?: string\n  } = {}) {\n    if (this.isLoggedIn) return\n\n    // Go to homepage\n    await this.page.goto(environmentUrl)\n    await this.signinButton.click()\n\n    // On built-in signin page, select Keycloak\n    await this.page.getByText(\"Keycloak\").click()\n\n    // Use keycloak POM to login\n    await this.keycloak.login({ username, password })\n\n    // Ensure we've landed back at the dev app logged in\n    const session = await this.page.locator(\"pre\").textContent()\n\n    expect(JSON.parse(session ?? \"{}\").user.name).toEqual(\"bob\")\n\n    this.isLoggedIn = true\n  }\n\n  async getSession({\n    environmentUrl = this.#auth.environmentUrl,\n  }: {\n    environmentUrl?: string\n  } = {}) {\n    if (!this.isLoggedIn) return\n\n    try {\n      const response = await this.page.goto(`${environmentUrl}/auth/session`)\n      if (response?.ok()) {\n        this.session = await response?.json()\n      }\n    } catch (error) {\n      console.error(error)\n    }\n  }\n}\n"
  },
  {
    "path": "packages/next-auth/test/e2e/helpers/authTest.ts",
    "content": "import { test as base } from \"@playwright/test\"\nimport { AuthFixture, createAuthFixture } from \"../fixtures/auth\"\nimport { WebApp } from \"../fixtures/webApp\"\nimport { KeycloakLoginPom } from \"../poms/keycloakLoginPom\"\n\ntype AuthJsWebappFixtures = {\n  auth: AuthFixture\n  keycloak: KeycloakLoginPom\n  webapp: WebApp\n}\n\nexport const test = base.extend<AuthJsWebappFixtures>({\n  auth: async ({}, use) => {\n    await use(createAuthFixture())\n  },\n  keycloak: async ({ page, auth }, use) => {\n    await use(new KeycloakLoginPom({ page, auth }))\n  },\n  webapp: async ({ page, context, auth, keycloak }, use) => {\n    await use(new WebApp({ page, context, auth, keycloak }))\n  },\n})\n\nexport { expect } from \"@playwright/test\"\n"
  },
  {
    "path": "packages/next-auth/test/e2e/poms/keycloakLoginPom.ts",
    "content": "import { expect, type Locator, type Page } from \"@playwright/test\"\nimport type { AuthFixture } from \"../fixtures/auth\"\n\nexport class KeycloakLoginPom {\n  usernameInput: Locator\n  passwordInput: Locator\n  signinButton: Locator\n\n  #auth: AuthFixture\n\n  constructor({ page, auth }: { page: Page; auth: AuthFixture }) {\n    this.#auth = auth\n\n    this.usernameInput = page.getByLabel(\"Username or email\")\n    this.passwordInput = page.locator(\"#password\")\n\n    this.signinButton = page.getByRole(\"button\", { name: \"Sign In\" })\n  }\n\n  async login({\n    username = this.#auth.loginUser,\n    password = this.#auth.loginPassword,\n  }: {\n    username?: string\n    password?: string\n  } = {}) {\n    if (!username) throw new Error(\"Keycloak username missing\")\n    if (!password) throw new Error(\"Keycloak password missing\")\n\n    await this.isVisible()\n\n    await this.usernameInput.fill(username)\n    await this.passwordInput.fill(password)\n\n    return this.signinButton.click()\n  }\n\n  isVisible() {\n    return Promise.all([\n      expect(this.usernameInput).toBeVisible(),\n      expect(this.passwordInput).toBeVisible(),\n      expect(this.signinButton).toBeVisible(),\n    ])\n  }\n}\n"
  },
  {
    "path": "packages/next-auth/test/e2e/tests/api/session.spec.ts",
    "content": "import { expect, test } from \"../../helpers/authTest\"\n\ntest.describe(\"user profile\", () => {\n  test(\"profile values\", async ({ webapp }) => {\n    try {\n      await webapp.login()\n      await webapp.getSession()\n      expect(webapp.session.user.sub).toMatch(\n        /^[a-f\\d]{4}(?:[a-f\\d]{4}-){4}[a-f\\d]{12}$/i\n      )\n    } catch (error) {\n      console.error(error)\n    }\n  })\n})\n"
  },
  {
    "path": "packages/next-auth/test/e2e/tests/providers/credentials.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\"\n\ntest.describe(\"Credentials Provider\", () => {\n  test(\"Signin / Signout\", async ({ page }) => {\n    await test.step(\"should login\", async () => {\n      await page.goto(\"http://localhost:3000/auth/signin\")\n      await page.getByLabel(\"Password\").fill(\"password\")\n      await page\n        .getByRole(\"button\", { name: \"Sign in with Credentials\" })\n        .click()\n      const session = await page.locator(\"pre\").textContent()\n\n      expect(JSON.parse(session ?? \"{}\")).toEqual({\n        user: {\n          email: \"test@example.com\",\n          name: \"Test User\",\n        },\n        expires: expect.any(String),\n      })\n    })\n\n    await test.step(\"should logout\", async () => {\n      await page\n        .getByRole(\"banner\")\n        .getByRole(\"button\", { name: \"Sign out\" })\n        .click()\n\n      // Wait on server-side signout req\n      await page.waitForTimeout(1000)\n\n      const session = await page.locator(\"pre\").textContent()\n      expect(JSON.parse(session ?? \"{}\")).toBeNull()\n    })\n  })\n})\n"
  },
  {
    "path": "packages/next-auth/test/e2e/tests/providers/keycloak.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\"\n\ntest.describe(\"KeyCloak Provider\", () => {\n  test(\"Signin / Signout\", async ({ page }) => {\n    if (\n      !process.env.TEST_KEYCLOAK_USERNAME ||\n      !process.env.TEST_KEYCLOAK_PASSWORD\n    )\n      throw new TypeError(\"Missing TEST_KEYCLOAK_{USERNAME,PASSWORD}\")\n\n    await test.step(\"should login\", async () => {\n      await page.goto(\"http://localhost:3000/auth/signin\")\n      await page.getByText(\"Keycloak\").click()\n      // Keycloak-hosted login form\n      await page\n        .getByLabel(\"Username or email\")\n        .fill(process.env.TEST_KEYCLOAK_USERNAME!)\n      await page.locator(\"#password\").fill(process.env.TEST_KEYCLOAK_PASSWORD!)\n      await page.getByRole(\"button\", { name: \"Sign In\" }).click()\n\n      // Should return to dev app\n      await page.waitForTimeout(1000)\n      const session = await page.locator(\"pre\").textContent()\n\n      expect(JSON.parse(session ?? \"{}\")).toEqual({\n        user: {\n          name: \"bob\",\n        },\n        expires: expect.any(String),\n      })\n    })\n\n    await test.step(\"should logout\", async () => {\n      // TODO: Enable the test\n      test.skip(\n        true,\n        \"The session isn't cleared after signout, until the next page load\"\n      )\n      await page\n        .getByRole(\"banner\")\n        .getByRole(\"button\", { name: \"Sign out\" })\n        .click()\n\n      // Wait on server-side signout req\n      await page.waitForTimeout(1000)\n\n      const session = await page.locator(\"pre\").textContent()\n      expect(JSON.parse(session ?? \"{}\")).toBeNull()\n    })\n  })\n})\n"
  },
  {
    "path": "packages/next-auth/test/env.test.ts",
    "content": "import { describe, it, expect, vi, beforeEach, afterEach } from \"vitest\"\nimport { NextRequest } from \"next/server.js\"\n\nimport { reqWithEnvURL, setEnvDefaults } from \"../lib/env\"\nimport { setEnvDefaults as coreSetEnvDefaults } from \"@auth/core\"\nimport type { NextAuthConfig } from \"../lib/index.js\"\n\nvi.mock(\"next/server.js\", () => ({\n  NextRequest: vi.fn(),\n}))\n\nvi.mock(\"@auth/core\", () => ({\n  setEnvDefaults: vi.fn(),\n}))\n\ndescribe(\"env\", () => {\n  beforeEach(() => {\n    vi.resetModules()\n    vi.resetAllMocks()\n  })\n\n  afterEach(() => {\n    vi.unstubAllEnvs()\n  })\n\n  describe(\"reqWithEnvURL\", () => {\n    it(\"should return the original request if AUTH_URL and NEXTAUTH_URL are not set\", () => {\n      const mockReq = {\n        nextUrl: { href: \"http://example.com\", origin: \"http://example.com\" },\n      }\n      const result = reqWithEnvURL(mockReq as NextRequest)\n\n      expect(result).toBe(mockReq)\n    })\n\n    it(\"should return a new request with modified URL if AUTH_URL is set\", () => {\n      vi.stubEnv(\"AUTH_URL\", \"http://auth.example.com\")\n\n      const mockReq = {\n        nextUrl: {\n          href: \"http://example.com/path\",\n          origin: \"http://example.com\",\n        },\n      }\n      const mockNewReq = {}\n      vi.mocked(NextRequest).mockReturnValue(mockNewReq as NextRequest)\n\n      const result = reqWithEnvURL(mockReq as NextRequest)\n\n      expect(NextRequest).toHaveBeenCalledWith(\n        \"http://auth.example.com/path\",\n        mockReq\n      )\n      expect(result).toBe(mockNewReq)\n    })\n  })\n\n  describe(\"setEnvDefaults\", () => {\n    it(\"should set secret from AUTH_SECRET\", () => {\n      vi.stubEnv(\"AUTH_SECRET\", \"test-secret\")\n\n      const config = {} as NextAuthConfig\n      setEnvDefaults(config)\n\n      expect(config.secret).toBe(\"test-secret\")\n      expect(coreSetEnvDefaults).toHaveBeenCalledWith(process.env, config, true)\n    })\n\n    it(\"should set secret from NEXTAUTH_SECRET if AUTH_SECRET is not set\", () => {\n      vi.stubEnv(\"NEXTAUTH_SECRET\", \"next-auth-secret\")\n\n      const config = {} as NextAuthConfig\n      setEnvDefaults(config)\n\n      expect(config.secret).toBe(\"next-auth-secret\")\n      expect(coreSetEnvDefaults).toHaveBeenCalledWith(process.env, config, true)\n    })\n\n    it(\"should not override existing secret in config\", () => {\n      vi.stubEnv(\"AUTH_SECRET\", \"test-secret\")\n\n      const config = { secret: \"existing-secret\" } as NextAuthConfig\n      setEnvDefaults(config)\n\n      expect(config.secret).toBe(\"existing-secret\")\n      expect(coreSetEnvDefaults).toHaveBeenCalledWith(process.env, config, true)\n    })\n\n    it(\"should prioritize AUTH_SECRET over NEXTAUTH_SECRET\", () => {\n      vi.stubEnv(\"AUTH_SECRET\", \"auth-secret\")\n      vi.stubEnv(\"NEXTAUTH_SECRET\", \"nextauth-secret\")\n\n      const config = {} as NextAuthConfig\n      setEnvDefaults(config)\n\n      expect(config.secret).toBe(\"auth-secret\")\n    })\n\n    it(\"should set basePath from AUTH_URL\", () => {\n      vi.stubEnv(\"AUTH_URL\", \"http://example.com/custom-auth\")\n\n      const config = {} as NextAuthConfig\n      setEnvDefaults(config)\n\n      expect(config.basePath).toBe(\"/custom-auth\")\n      expect(coreSetEnvDefaults).toHaveBeenCalledWith(process.env, config, true)\n    })\n\n    it(\"should set basePath from NEXTAUTH_URL if AUTH_URL is not set\", () => {\n      vi.stubEnv(\"NEXTAUTH_URL\", \"http://example.com/next-auth\")\n\n      const config = {} as NextAuthConfig\n      setEnvDefaults(config)\n\n      expect(config.basePath).toBe(\"/next-auth\")\n      expect(coreSetEnvDefaults).toHaveBeenCalledWith(process.env, config, true)\n    })\n\n    it('should not set basePath if URL pathname is \"/\"', () => {\n      vi.stubEnv(\"AUTH_URL\", \"http://example.com/\")\n\n      const config = {} as NextAuthConfig\n      setEnvDefaults(config)\n\n      expect(config.basePath).toBe(\"/api/auth\")\n      expect(coreSetEnvDefaults).toHaveBeenCalledWith(process.env, config, true)\n    })\n\n    it(\"should not override existing basePath in config\", () => {\n      vi.stubEnv(\"AUTH_URL\", \"http://example.com/custom-auth\")\n\n      const config = { basePath: \"/existing-path\" } as NextAuthConfig\n      setEnvDefaults(config)\n\n      expect(config.basePath).toBe(\"/existing-path\")\n      expect(coreSetEnvDefaults).toHaveBeenCalledWith(process.env, config, true)\n    })\n\n    it(\"should prioritize AUTH_URL over NEXTAUTH_URL\", () => {\n      vi.stubEnv(\"AUTH_URL\", \"http://example.com/auth-url\")\n      vi.stubEnv(\"NEXTAUTH_URL\", \"http://example.com/nextauth-url\")\n\n      const config = {} as NextAuthConfig\n      setEnvDefaults(config)\n\n      expect(config.basePath).toBe(\"/auth-url\")\n    })\n\n    it(\"should default basePath to /api/auth if no URL is set\", () => {\n      const config = {} as NextAuthConfig\n      setEnvDefaults(config)\n\n      expect(config.basePath).toBe(\"/api/auth\")\n      expect(coreSetEnvDefaults).toHaveBeenCalledWith(process.env, config, true)\n    })\n\n    it(\"should handle invalid URL gracefully\", () => {\n      vi.stubEnv(\"AUTH_URL\", \"invalid-url\")\n\n      const config = {} as NextAuthConfig\n      setEnvDefaults(config)\n\n      expect(config.basePath).toBe(\"/api/auth\")\n      expect(coreSetEnvDefaults).toHaveBeenCalledWith(process.env, config, true)\n    })\n  })\n})\n"
  },
  {
    "path": "packages/next-auth/tsconfig.json",
    "content": "{\n  \"extends\": \"../utils/tsconfig.json\",\n  \"compilerOptions\": {\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"jsx\": \"react-jsx\",\n    \"outDir\": \".\",\n    \"rootDir\": \"src\"\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"*.js\", \"*.d.ts\"]\n}\n"
  },
  {
    "path": "packages/next-auth/typedoc.config.cjs",
    "content": "// @ts-check\n\n/**\n * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').PluginOptions}\n */\nmodule.exports = {\n  entryPoints: [\"src/index.ts\", \"src/adapters.ts\", \"src/middleware.ts\", \"src/jwt.ts\", \"src/next.ts\", \"src/react.tsx\", \"src/webauthn.ts\"],\n  entryPointStrategy: \"expand\",\n  tsconfig: \"./tsconfig.json\",\n  entryModule: \"next-auth\",\n  entryFileName: \"../nextjs.mdx\",\n  includeVersion: true,\n  readme: 'none'\n}\n"
  },
  {
    "path": "packages/utils/adapter.ts",
    "content": "import { afterAll, beforeAll, expect, test } from \"vitest\"\n\nimport type { Adapter, VerificationToken } from \"@auth/core/adapters\"\nimport { createHash, randomInt, randomUUID } from \"crypto\"\n\nexport interface TestOptions {\n  adapter: Adapter\n  fixtures?: {\n    user?: any\n    session?: any\n    account?: any\n    sessionUpdateExpires?: Date\n    verificationTokenExpires?: Date\n  }\n  db: {\n    /** Generates UUID v4 by default. Use it to override how the test suite should generate IDs, like user id. */\n    id?: () => string | undefined\n    /**\n     * Manually disconnect database after all tests have been run,\n     * if your adapter doesn't do it automatically\n     */\n    disconnect?: () => Promise<any>\n    /**\n     * Manually establishes a db connection before all tests,\n     * if your db doesn't do this automatically\n     */\n    connect?: () => Promise<any>\n    /** A simple query function that returns a session directly from the db. */\n    session: (sessionToken: string) => any\n    /** A simple query function that returns a user directly from the db. */\n    user: (id: string) => any\n    /** A simple query function that returns an account directly from the db. */\n    account: (providerAccountId: {\n      provider: string\n      providerAccountId: string\n    }) => any\n    /**\n     * A simple query function that returns an verification token directly from the db,\n     * based on the user identifier and the verification token (hashed).\n     */\n    verificationToken: (params: { identifier: string; token: string }) => any\n    /**\n     * A simple query function that returns an authenticator directly from the db.\n     */\n    authenticator?: (credentialID: string) => any\n  }\n  skipTests?: string[]\n  /**\n   * Enables testing of WebAuthn methods.\n   */\n  testWebAuthnMethods?: boolean\n}\n\n/**\n * A wrapper to run the most basic tests.\n * Run this at the top of your test file.\n * You can add additional tests below, if you wish.\n */\nexport async function runBasicTests(options: TestOptions) {\n  const id = () => options.db.id?.() ?? randomUUID()\n  // Init\n  beforeAll(async () => {\n    await options.db.connect?.()\n  })\n\n  const {\n    adapter: _adapter,\n    db,\n    skipTests: skipTests = [],\n    testWebAuthnMethods,\n  } = options\n  const adapter = _adapter as Required<Adapter>\n\n  if (!testWebAuthnMethods) {\n    skipTests.push(\n      ...[\n        \"getAccount\",\n        \"getAuthenticator\",\n        \"createAuthenticator\",\n        \"listAuthenticatorsByUserId\",\n        \"updateAuthenticatorCounter\",\n      ]\n    )\n  }\n\n  const maybeTest = (\n    method: keyof Adapter,\n    ...args: Parameters<typeof test> extends [any, ...infer U] ? U : never\n  ) =>\n    skipTests.includes(method)\n      ? test.skip(method, ...args)\n      : test(method, ...args)\n\n  afterAll(async () => {\n    // @ts-expect-error This is only used for the TypeORM adapter\n    await adapter.__disconnect?.()\n    await options.db.disconnect?.()\n  })\n\n  let user = options.fixtures?.user ?? {\n    id: id(),\n    email: \"fill@murray.com\",\n    image: \"https://www.fillmurray.com/460/300\",\n    name: \"Fill Murray\",\n    emailVerified: new Date(),\n  }\n\n  if (process.env.CUSTOM_MODEL === \"1\") {\n    user.role = \"admin\"\n    user.phone = \"00000000000\"\n  }\n\n  const session: any = options.fixtures?.session ?? {\n    sessionToken: id(),\n    expires: ONE_WEEK_FROM_NOW,\n  }\n\n  const account: any = options.fixtures?.account ?? {\n    provider: \"github\",\n    providerAccountId: id(),\n    type: \"oauth\",\n    access_token: id(),\n    expires_at: ONE_MONTH / 1000,\n    id_token: id(),\n    refresh_token: id(),\n    token_type: \"bearer\",\n    scope: \"user\",\n    session_state: id(),\n  }\n\n  // All adapters must define these methods\n\n  test(\"Required (User, Account, Session) methods exist\", () => {\n    const requiredMethods = [\n      \"createUser\",\n      \"getUser\",\n      \"getUserByEmail\",\n      \"getUserByAccount\",\n      \"updateUser\",\n      \"linkAccount\",\n      \"createSession\",\n      \"getSessionAndUser\",\n      \"updateSession\",\n      \"deleteSession\",\n    ]\n    requiredMethods.forEach((method) => {\n      expect(adapter).toHaveProperty(method)\n    })\n  })\n\n  test(\"createUser\", async () => {\n    const { id } = await adapter.createUser(user)\n    const dbUser = await db.user(id)\n    expect(dbUser).toEqual({ ...user, id })\n    user = dbUser\n    session.userId = dbUser.id\n    account.userId = dbUser.id\n  })\n\n  test(\"getUser\", async () => {\n    expect(await adapter.getUser(id())).toBeNull()\n    expect(await adapter.getUser(user.id)).toEqual(user)\n  })\n\n  test(\"getUserByEmail\", async () => {\n    expect(await adapter.getUserByEmail(\"non-existent-email\")).toBeNull()\n    expect(await adapter.getUserByEmail(user.email)).toEqual(user)\n  })\n\n  test(\"createSession\", async () => {\n    const { sessionToken } = await adapter.createSession(session)\n    const dbSession = await db.session(sessionToken)\n\n    expect(dbSession).toEqual({ ...session, id: dbSession.id })\n    session.userId = dbSession.userId\n    session.id = dbSession.id\n  })\n\n  test(\"getSessionAndUser\", async () => {\n    let sessionAndUser = await adapter.getSessionAndUser(\"invalid-token\")\n    expect(sessionAndUser).toBeNull()\n\n    sessionAndUser = await adapter.getSessionAndUser(session.sessionToken)\n    if (!sessionAndUser) {\n      throw new Error(\"Session and User was not found, but they should exist\")\n    }\n    expect(sessionAndUser).toEqual({\n      user,\n      session,\n    })\n  })\n\n  test(\"updateUser\", async () => {\n    const newName = \"Updated Name\"\n    const returnedUser = await adapter.updateUser({\n      id: user.id,\n      name: newName,\n    })\n    expect(returnedUser.name).toBe(newName)\n\n    const dbUser = await db.user(user.id)\n    expect(dbUser.name).toBe(newName)\n    user.name = newName\n  })\n\n  test(\"updateSession\", async () => {\n    let dbSession = await db.session(session.sessionToken)\n\n    const expires = options.fixtures?.sessionUpdateExpires ?? ONE_MONTH_FROM_NOW\n\n    expect(dbSession.expires.valueOf()).not.toBe(expires.valueOf())\n\n    await adapter.updateSession({\n      sessionToken: session.sessionToken,\n      expires,\n    })\n\n    dbSession = await db.session(session.sessionToken)\n    expect(dbSession.expires.valueOf()).toBe(expires.valueOf())\n  })\n\n  test(\"linkAccount\", async () => {\n    await adapter.linkAccount(account)\n    const dbAccount = await db.account({\n      provider: account.provider,\n      providerAccountId: account.providerAccountId,\n    })\n    expect(dbAccount).toEqual({ ...account, id: dbAccount.id })\n  })\n\n  test(\"getUserByAccount\", async () => {\n    let userByAccount = await adapter.getUserByAccount({\n      provider: \"invalid-provider\",\n      providerAccountId: \"invalid-provider-account-id\",\n    })\n    expect(userByAccount).toBeNull()\n\n    userByAccount = await adapter.getUserByAccount({\n      provider: account.provider,\n      providerAccountId: account.providerAccountId,\n    })\n    expect(userByAccount).toEqual(user)\n  })\n\n  test(\"deleteSession\", async () => {\n    await adapter.deleteSession(session.sessionToken)\n    const dbSession = await db.session(session.sessionToken)\n    expect(dbSession).toBeNull()\n  })\n\n  // These are optional for custom adapters, but we require them for the official adapters\n\n  test(\"Verification Token methods exist\", () => {\n    const requiredMethods = [\"createVerificationToken\", \"useVerificationToken\"]\n    requiredMethods.forEach((method) => {\n      expect(adapter).toHaveProperty(method)\n    })\n  })\n\n  test(\"createVerificationToken\", async () => {\n    const identifier = \"info@example.com\"\n    const token = id()\n    const hashedToken = hashToken(token)\n\n    const verificationToken = {\n      token: hashedToken,\n      identifier,\n      expires:\n        options.fixtures?.verificationTokenExpires ?? FIFTEEN_MINUTES_FROM_NOW,\n    }\n    await adapter.createVerificationToken?.(verificationToken)\n\n    const dbVerificationToken = await db.verificationToken({\n      token: hashedToken,\n      identifier,\n    })\n\n    expect(dbVerificationToken).toEqual(verificationToken)\n  })\n\n  test(\"useVerificationToken\", async () => {\n    const identifier = \"info@example.com\"\n    const token = id()\n    const hashedToken = hashToken(token)\n    const verificationToken = {\n      token: hashedToken,\n      identifier,\n      expires:\n        options.fixtures?.verificationTokenExpires ?? FIFTEEN_MINUTES_FROM_NOW,\n    } satisfies VerificationToken\n    await adapter.createVerificationToken?.(verificationToken)\n\n    const dbVerificationToken1 = await adapter.useVerificationToken?.({\n      identifier,\n      token: hashedToken,\n    })\n\n    if (!dbVerificationToken1) {\n      throw new Error(\"Verification Token was not found, but it should exist\")\n    }\n\n    expect(dbVerificationToken1).toEqual(verificationToken)\n\n    const dbVerificationTokenSecondTry = await adapter.useVerificationToken?.({\n      identifier,\n      token: hashedToken,\n    })\n\n    expect(dbVerificationTokenSecondTry).toBeNull()\n\n    // Should only return if the identifier matches\n\n    const verificationToken2 = {\n      token: hashedToken,\n      identifier,\n      expires:\n        options.fixtures?.verificationTokenExpires ?? FIFTEEN_MINUTES_FROM_NOW,\n    } satisfies VerificationToken\n\n    await adapter.createVerificationToken?.(verificationToken2)\n\n    const dbVerificationToken2 = await adapter.useVerificationToken?.({\n      identifier: \"invalid@identifier.com\",\n      token: hashedToken,\n    })\n\n    expect(dbVerificationToken2).toBeNull()\n  })\n\n  // Future methods\n  // These methods are not yet invoked in the core, but built-in adapters must implement them\n  test(\"Future methods exist\", () => {\n    const requiredMethods = [\"unlinkAccount\", \"deleteUser\"]\n    requiredMethods.forEach((method) => {\n      expect(adapter).toHaveProperty(method)\n    })\n  })\n\n  test(\"unlinkAccount\", async () => {\n    let dbAccount = await db.account({\n      provider: account.provider,\n      providerAccountId: account.providerAccountId,\n    })\n    expect(dbAccount).toEqual({ ...account, id: dbAccount.id })\n\n    await adapter.unlinkAccount?.({\n      provider: account.provider,\n      providerAccountId: account.providerAccountId,\n    })\n    dbAccount = await db.account({\n      provider: account.provider,\n      providerAccountId: account.providerAccountId,\n    })\n    expect(dbAccount).toBeNull()\n  })\n\n  maybeTest(\"deleteUser\", async () => {\n    let dbUser = await db.user(user.id)\n    expect(dbUser).toEqual(user)\n\n    // Re-populate db with session and account\n    delete session.id\n    await adapter.createSession(session)\n    await adapter.linkAccount(account)\n\n    await adapter.deleteUser?.(user.id)\n    dbUser = await db.user(user.id)\n    // User should not exist after it is deleted\n    expect(dbUser).toBeNull()\n\n    const dbSession = await db.session(session.sessionToken)\n    // Session should not exist after user is deleted\n    expect(dbSession).toBeNull()\n\n    const dbAccount = await db.account({\n      provider: account.provider,\n      providerAccountId: account.providerAccountId,\n    })\n    // Account should not exist after user is deleted\n    expect(dbAccount).toBeNull()\n  })\n\n  maybeTest(\"getAccount\", async () => {\n    // Setup\n    const providerAccountId = randomUUID()\n    const provider = \"auth0\"\n    const localUser = await adapter.createUser({\n      id: id(),\n      email: \"getAccount@example.com\",\n      emailVerified: null,\n    })\n    await adapter.linkAccount({\n      provider,\n      providerAccountId,\n      type: \"oauth\",\n      userId: localUser.id,\n    })\n\n    // Test\n    const invalidBoth = await adapter.getAccount(\n      \"invalid-provider-account-id\",\n      \"invalid-provider\"\n    )\n    expect(invalidBoth).toBeNull()\n    const invalidProvider = await adapter.getAccount(\n      providerAccountId,\n      \"invalid-provider\"\n    )\n    expect(invalidProvider).toBeNull()\n    const invalidProviderAccountId = await adapter.getAccount(\n      \"invalid-provider-account-id\",\n      provider\n    )\n    expect(invalidProviderAccountId).toBeNull()\n    const validAccount = await adapter.getAccount(providerAccountId, provider)\n    expect(validAccount).not.toBeNull()\n\n    const dbAccount = await db.account({\n      provider,\n      providerAccountId,\n    })\n    expect(dbAccount).toMatchObject(validAccount || {})\n  })\n  maybeTest(\"createAuthenticator\", async () => {\n    // Setup\n    const credentialID = randomUUID()\n    const localUser = await adapter.createUser({\n      id: id(),\n      email: \"createAuthenticator@example.com\",\n      emailVerified: null,\n    })\n    await adapter.linkAccount({\n      provider: \"webauthn\",\n      providerAccountId: credentialID,\n      type: \"webauthn\",\n      userId: localUser.id,\n    })\n\n    // Test\n    const authenticatorData = {\n      credentialID,\n      providerAccountId: credentialID,\n      userId: localUser.id,\n      counter: randomInt(100),\n      credentialBackedUp: true,\n      credentialDeviceType: \"platform\",\n      credentialPublicKey: randomUUID(),\n      transports: \"usb,ble,nfc\",\n    }\n    const newAuthenticator =\n      await adapter.createAuthenticator(authenticatorData)\n    expect(newAuthenticator).not.toBeNull()\n    expect(newAuthenticator).toMatchObject(authenticatorData)\n\n    const dbAuthenticator = db.authenticator\n      ? await db.authenticator(credentialID)\n      : undefined\n    expect(dbAuthenticator).toMatchObject(newAuthenticator)\n  })\n  maybeTest(\"getAuthenticator\", async () => {\n    // Setup\n    const credentialID = randomUUID()\n    const localUser = await adapter.createUser({\n      id: id(),\n      email: \"getAuthenticator@example.com\",\n      emailVerified: null,\n    })\n    await adapter.linkAccount({\n      provider: \"webauthn\",\n      providerAccountId: credentialID,\n      type: \"webauthn\",\n      userId: localUser.id,\n    })\n    await adapter.createAuthenticator({\n      credentialID,\n      providerAccountId: credentialID,\n      userId: localUser.id,\n      counter: randomInt(100),\n      credentialBackedUp: true,\n      credentialDeviceType: \"platform\",\n      credentialPublicKey: randomUUID(),\n      transports: \"usb,ble,nfc\",\n    })\n\n    // Test\n    const invalidAuthenticator = await adapter.getAuthenticator(\n      \"invalid-credential-id\"\n    )\n    expect(invalidAuthenticator).toBeNull()\n\n    const validAuthenticator = await adapter.getAuthenticator(credentialID)\n    expect(validAuthenticator).not.toBeNull()\n    const dbAuthenticator = db.authenticator\n      ? await db.authenticator(credentialID)\n      : undefined\n    expect(dbAuthenticator).toMatchObject(validAuthenticator || {})\n  })\n  maybeTest(\"listAuthenticatorsByUserId\", async () => {\n    // Setup\n    const user1 = await adapter.createUser({\n      id: id(),\n      email: \"listAuthenticatorsByUserId1@example.com\",\n      emailVerified: null,\n    })\n    const user2 = await adapter.createUser({\n      id: id(),\n      email: \"listAuthenticatorsByUserId2@example.com\",\n      emailVerified: null,\n    })\n    const credentialID1 = randomUUID()\n    const credentialID2 = randomUUID()\n    const credentialID3 = randomUUID()\n    await adapter.linkAccount({\n      provider: \"webauthn\",\n      providerAccountId: credentialID1,\n      type: \"webauthn\",\n      userId: user1.id,\n    })\n    await adapter.linkAccount({\n      provider: \"webauthn\",\n      providerAccountId: credentialID2,\n      type: \"webauthn\",\n      userId: user1.id,\n    })\n    await adapter.linkAccount({\n      provider: \"webauthn\",\n      providerAccountId: credentialID3,\n      type: \"webauthn\",\n      userId: user2.id,\n    })\n    const authenticator1 = await adapter.createAuthenticator({\n      credentialID: credentialID1,\n      providerAccountId: credentialID1,\n      userId: user1.id,\n      counter: randomInt(100),\n      credentialBackedUp: true,\n      credentialDeviceType: \"platform\",\n      credentialPublicKey: randomUUID(),\n      transports: \"usb,ble,nfc\",\n    })\n    const authenticator2 = await adapter.createAuthenticator({\n      credentialID: credentialID2,\n      providerAccountId: credentialID2,\n      userId: user1.id,\n      counter: randomInt(100),\n      credentialBackedUp: true,\n      credentialDeviceType: \"platform\",\n      credentialPublicKey: randomUUID(),\n      transports: \"usb,nfc\",\n    })\n    const authenticator3 = await adapter.createAuthenticator({\n      credentialID: credentialID3,\n      providerAccountId: credentialID3,\n      userId: user2.id,\n      counter: randomInt(100),\n      credentialBackedUp: true,\n      credentialDeviceType: \"platform\",\n      credentialPublicKey: randomUUID(),\n      transports: \"usb,ble\",\n    })\n\n    // Test\n    const authenticators0 =\n      await adapter.listAuthenticatorsByUserId(\"invalid-user-id\")\n    expect(authenticators0).toEqual([])\n\n    const authenticators1 = await adapter.listAuthenticatorsByUserId(user1.id)\n    expect(authenticators1).not.toBeNull()\n    expect([authenticator2, authenticator1]).toEqual(\n      expect.arrayContaining(authenticators1 || [])\n    )\n\n    const authenticators2 = await adapter.listAuthenticatorsByUserId(user2.id)\n    expect(authenticators2).not.toBeNull()\n    expect([authenticator3]).toMatchObject(\n      expect.arrayContaining(authenticators2 || [])\n    )\n  })\n  maybeTest(\"updateAuthenticatorCounter\", async () => {\n    // Setup\n    const credentialID = randomUUID()\n    const localUser = await adapter.createUser({\n      id: id(),\n      email: \"updateAuthenticatorCounter@example.com\",\n      emailVerified: null,\n    })\n    await adapter.linkAccount({\n      provider: \"webauthn\",\n      providerAccountId: credentialID,\n      type: \"webauthn\",\n      userId: localUser.id,\n    })\n    const newAuthenticator = await adapter.createAuthenticator({\n      credentialID,\n      providerAccountId: credentialID,\n      userId: localUser.id,\n      counter: randomInt(100),\n      credentialBackedUp: true,\n      credentialDeviceType: \"platform\",\n      credentialPublicKey: randomUUID(),\n      transports: \"usb,ble,nfc\",\n    })\n\n    // Test\n    await expect(() =>\n      adapter.updateAuthenticatorCounter(\n        \"invalid-credential-id\",\n        randomInt(100)\n      )\n    ).rejects.toThrow()\n\n    const newCounter = newAuthenticator.counter + randomInt(100)\n    const updatedAuthenticator = await adapter.updateAuthenticatorCounter(\n      credentialID,\n      newCounter\n    )\n    expect(updatedAuthenticator).not.toBeNull()\n    expect(updatedAuthenticator.counter).toBe(newCounter)\n  })\n}\n\n// UTILS\nexport function hashToken(token: string) {\n  return createHash(\"sha256\").update(`${token}anything`).digest(\"hex\")\n}\n\nexport { randomUUID }\n\nexport const ONE_WEEK_FROM_NOW = new Date(Date.now() + 1000 * 60 * 60 * 24 * 7)\nONE_WEEK_FROM_NOW.setMilliseconds(0)\nexport const FIFTEEN_MINUTES_FROM_NOW = new Date(Date.now() + 15 * 60 * 1000)\nFIFTEEN_MINUTES_FROM_NOW.setMilliseconds(0)\nexport const ONE_MONTH = 1000 * 60 * 60 * 24 * 30\nexport const ONE_MONTH_FROM_NOW = new Date(Date.now() + ONE_MONTH)\nONE_MONTH_FROM_NOW.setMilliseconds(0)\n"
  },
  {
    "path": "packages/utils/package.json",
    "content": "{\n  \"name\": \"utils\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"devDependencies\": {\n    \"@auth/core\": \"workspace:*\",\n    \"@preact/preset-vite\": \"^2.8.1\",\n    \"dotenv\": \"^10.0.0\"\n  },\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"unplugin-swc\": \"^1.4.4\"\n  }\n}\n"
  },
  {
    "path": "packages/utils/scripts/providers.js",
    "content": "// Use this script to re-export all providers from core in Auth.js frameworks\n\nimport fs from \"fs/promises\"\nimport { resolve, join } from \"path\"\nimport { parseArgs } from \"node:util\"\n\nconst sourceDir = resolve(process.cwd(), \"../core/src/providers\")\n\nconst args = parseArgs({\n  args: process.argv.slice(2),\n  options: { out: { type: \"string\", default: \"src\" } },\n})\nconst { out } = args.values\n\nconst destinationDir = resolve(process.cwd(), join(out, \"providers\"))\nconst nonProvider = [\"provider-types.ts\", \"oauth.ts\"]\ntry {\n  await fs.mkdir(destinationDir, { recursive: true })\n  const files = (await fs.readdir(sourceDir)).filter(\n    (file) => !nonProvider.includes(file)\n  )\n  for (const file of files) {\n    const destinationPath = resolve(destinationDir, file)\n    const provider = file.substring(0, file.indexOf(\".\"))\n    let content = `export * from \"@auth/core/providers/${provider}\"`\n    if (provider !== \"index\") {\n      content += `\\nexport { default } from \"@auth/core/providers/${provider}\"`\n    }\n\n    content = content.replace(/\\/index/g, \"\")\n    await fs.writeFile(destinationPath, content)\n  }\n  console.log(\"All files copied successfully!\")\n} catch (error) {\n  console.error(\"Error occurred while copying files:\", error)\n}\n"
  },
  {
    "path": "packages/utils/scripts/setup-fw-integration.js",
    "content": "import { promises as fs } from \"fs\"\nimport path from \"path\"\n\nconst __dirname = path.dirname(new URL(import.meta.url).pathname)\n\n// Check if name is provided\nif (process.argv.length < 3) {\n  console.error(\"Error: No name provided\")\n  process.exit(1)\n}\n\nconst name = process.argv[2]\n// Convert name to lowercase\nconst id = name\n  .toLowerCase()\n  // replace dots with empty string & replace spaces with dashes\n  .replace(/\\./g, \"\")\n  .replace(/\\s/g, \"-\")\n\n// Delete directory if it exists\ntry {\n  await fs.rm(path.join(__dirname, \"../..\", `frameworks-${id}`), {\n    recursive: true,\n  })\n} catch {\n  // ignore\n}\n\n// Copy directory\nconst sourceDir = path.join(__dirname, \"../..\", \"frameworks-template\")\nconst destinationDir = path.join(__dirname, \"../..\", `frameworks-${id}`)\nawait fs.mkdir(destinationDir)\n\nconsole.log(`📂 Copying ${sourceDir} to ${destinationDir}`)\n\n// copy the whole directory recursively\nawait fs.cp(sourceDir, destinationDir, { recursive: true })\n\n// Delete node_modules & providers\nawait fs.rm(path.join(destinationDir, \"node_modules\"), { recursive: true })\nawait fs.rm(path.join(destinationDir, \"providers\"), { recursive: true })\n\n// Replace placeholders in files\nconst files = await fs.readdir(destinationDir, { withFileTypes: true })\nfor (const file of files) {\n  if (file.isFile()) {\n    const filePath = path.join(destinationDir, file.name)\n    let content = await fs.readFile(filePath, \"utf8\")\n    content = content\n      .replace(/<framework-id>/g, id)\n      .replace(/<framework-name>/g, name)\n    await fs.writeFile(filePath, content, \"utf8\")\n  }\n}\n\n// Rename `name` field in package.json\nconst packageJsonPath = path.join(destinationDir, \"package.json\")\nlet packageJson = JSON.parse(await fs.readFile(packageJsonPath, \"utf8\"))\npackageJson.name = `@auth/frameworks-${id}`\nawait fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2))\n\n// Update redirects in vercel.json\nconst vercelJsonPath = path.join(__dirname, \"../../..\", \"docs\", \"vercel.json\")\nlet vercelJson = JSON.parse(await fs.readFile(vercelJsonPath, \"utf8\"))\nvercelJson.redirects = [\n  ...vercelJson.redirects,\n  {\n    source: \"/\",\n    has: [{ type: \"host\", value: `${id}.authjs.dev` }],\n    destination: `https://authjs.dev/reference/${id}`,\n  },\n]\nawait fs.writeFile(vercelJsonPath, JSON.stringify(vercelJson, null, 2))\n\n// add a new line in pr-labeler.yml\nconst prLabelerPath = path.join(\n  __dirname,\n  \"../../..\",\n  \".github\",\n  \"pr-labeler.yml\"\n)\nlet prLabeler = await fs.readFile(prLabelerPath, \"utf8\")\nconst newEntry = 'frameworkId: [\"packages/frameworks-frameworkId/**/*\"]\\n'\nprLabeler += newEntry\n// replace frameworkId with the id\nprLabeler = prLabeler.replace(/frameworkId/g, id)\n\nawait fs.writeFile(prLabelerPath, prLabeler)\n\nconsole.log(\"✅ Success. Please run `pnpm i` to install dependencies.\")\n"
  },
  {
    "path": "packages/utils/tsconfig.eslint.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"include\": [\"../../packages/**/*\", \"../../apps/**/*\"]\n}\n"
  },
  {
    "path": "packages/utils/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"baseUrl\": \".\",\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"emitDecoratorMetadata\": true,\n    \"experimentalDecorators\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"isolatedModules\": true,\n    \"module\": \"NodeNext\",\n    \"moduleResolution\": \"NodeNext\",\n    \"skipDefaultLibCheck\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"strictNullChecks\": true,\n    \"stripInternal\": true,\n    \"target\": \"es2020\"\n  }\n}\n"
  },
  {
    "path": "packages/utils/vitest-setup.ts",
    "content": "// TODO: Drop this file when Node.js 18 is deprecated\nglobalThis.crypto ??= require(\"node:crypto\").webcrypto\n"
  },
  {
    "path": "packages/utils/vitest.config.ts",
    "content": "/// <reference types=\"vitest\" />\n\nimport { defineConfig } from \"vite\"\nimport swc from \"unplugin-swc\"\nimport preact from \"@preact/preset-vite\"\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  test: {\n    // NOTE: `.spec` is reserved for Playwright tests\n    include: [\"**/*.test.?(c|m)[jt]s?(x)\"],\n    coverage: {\n      all: true,\n      enabled: true,\n      include: [\"src\"],\n      reporter: [\"json\", \"html\"],\n    },\n    setupFiles: [\"../utils/vitest-setup.ts\"],\n  },\n  plugins: [swc.vite(), preact({ include: [\"**/*[jt]sx\"] })],\n})\n"
  },
  {
    "path": "patches/@balazsorban__monorepo-release@0.5.1.patch",
    "content": "diff --git a/dist/index.js b/dist/index.js\nindex a6899bbf1a545f9581a1f3c4b83e433e35c7cee9..329107182487c1b8321ff48374dcbeb1ebc86572 100755\n--- a/dist/index.js\n+++ b/dist/index.js\n@@ -5,7 +5,7 @@ import { publish } from \"./publish.js\";\n import { log } from \"./utils.js\";\n import { bold } from \"yoctocolors\";\n // TODO: Allow user config\n-const userConfig = {};\n+const userConfig = { ignorePackages: [\"next-auth\"] };\n const config = { ...defaultConfig, ...userConfig };\n const endMsg = bold(\"Done\");\n console.time(endMsg);\n"
  },
  {
    "path": "pnpm-workspace.yaml",
    "content": "packages:\n  - \"packages/*\"\n  - \"apps/dev/*\"\n  - \"docs\"\n"
  },
  {
    "path": "turbo.json",
    "content": "{\n  \"$schema\": \"https://turbo.build/schema.json\",\n  \"tasks\": {\n    \"build\": {\n      \"dependsOn\": [\"^build\"],\n      \"outputs\": [\n        \".next\",\n        \"dist/**\",\n        \"lib/**\",\n        \"providers/**\",\n        \"generated/**\",\n        \"*.js\",\n        \"*.d.ts\",\n        \"*.d.ts.map\",\n        \"src/lib/pages/styles.ts\",\n        \"src/providers/provider-types.ts\",\n        \".svelte-kit/**\"\n      ],\n      \"outputLogs\": \"new-only\"\n    },\n    \"clean\": {\n      \"cache\": false\n    },\n    \"dev\": {\n      \"cache\": false\n    },\n    \"test\": {\n      \"outputs\": [\"coverage/**\"],\n      \"inputs\": [\"src/**/\", \"test/**/\"],\n      \"outputLogs\": \"new-only\"\n    },\n    \"test:e2e\": {\n      \"dependsOn\": [\"next-auth#build\"],\n      \"outputs\": [\n        \"blob-report/**\",\n        \"playwright-report/**\",\n        \"playwright/.cache/**\",\n        \"test-results/**\"\n      ],\n      \"env\": [\n        \"TEST_KEYCLOAK_PASSWORD\",\n        \"TEST_KEYCLOAK_USERNAME\",\n        \"AUTH_SECRET\",\n        \"AUTH_KEYCLOAK_ID\",\n        \"AUTH_KEYCLOAK_SECRET\",\n        \"AUTH_KEYCLOAK_ISSUER\",\n        \"AUTH_TRUST_HOST\"\n      ]\n    },\n    \"@auth/xata-adapter#test\": {\n      \"passThroughEnv\": [\"XATA_API_KEY\", \"XATA_DATABASE_URL\"]\n    },\n    \"@auth/hasura-adapter#build\": {\n      \"dependsOn\": [\"@auth/core#build\"],\n      \"outputs\": [\n        \"lib/**\",\n        \"*.js\",\n        \"*.d.ts\",\n        \"*.d.ts.map\",\n        \"src/lib/generated/**\"\n      ]\n    },\n    \"docs#dev\": {\n      \"dependsOn\": [\n        \"next-auth#build\",\n        \"@auth/core#build\",\n        \"@auth/sveltekit#build\",\n        \"@auth/express#build\",\n        \"@auth/solid-start#build\",\n        \"@auth/qwik#build\"\n      ],\n      \"persistent\": true,\n      \"cache\": false,\n      \"outputs\": [\n        \".next/**/*\",\n        \"build/**/*\",\n        \"!.next/cache/**\",\n        \"docs/reference/**/*.mdx\",\n        \"docs/reference/*.mdx\"\n      ]\n    },\n    \"docs#build\": {\n      \"inputs\": [\n        \"pages/**\",\n        \"utils/**\",\n        \"public/**\",\n        \"components/**\",\n        \"theme.config.tsx\",\n        \"typedoc*\",\n        \"vercel.json\",\n        \"next-sitemap.config.cjs\",\n        \"next.config.js\"\n      ],\n      \"dependsOn\": [\n        \"@auth/core#build\",\n        \"@auth/azure-tables-adapter#build\",\n        \"@auth/d1-adapter#build\",\n        \"@auth/dgraph-adapter#build\",\n        \"@auth/drizzle-adapter#build\",\n        \"@auth/dynamodb-adapter#build\",\n        \"@auth/edgedb-adapter#build\",\n        \"@auth/fauna-adapter#build\",\n        \"@auth/firebase-adapter#build\",\n        \"@auth/hasura-adapter#build\",\n        \"@auth/kysely-adapter#build\",\n        \"@auth/mikro-orm-adapter#build\",\n        \"@auth/mongodb-adapter#build\",\n        \"@auth/neo4j-adapter#build\",\n        \"@auth/pg-adapter#build\",\n        \"@auth/pouchdb-adapter#build\",\n        \"@auth/prisma-adapter#build\",\n        \"@auth/sequelize-adapter#build\",\n        \"@auth/solid-start#build\",\n        \"@auth/supabase-adapter#build\",\n        \"@auth/surrealdb-adapter#build\",\n        \"@auth/sveltekit#build\",\n        \"@auth/typeorm-adapter#build\",\n        \"@auth/unstorage-adapter#build\",\n        \"@auth/upstash-redis-adapter#build\",\n        \"@auth/xata-adapter#build\",\n        \"@auth/qwik#build\",\n        \"next-auth#build\",\n        \"^build\"\n      ],\n      \"outputs\": [\n        \".next/**/*\",\n        \"build/**/*\",\n        \"!.next/cache/**\",\n        \"docs/reference/**/*.mdx\",\n        \"docs/reference/**/*.js\",\n        \"docs/reference/*.mdx\"\n      ]\n    }\n  }\n}\n"
  }
]