Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(frameworks): add @auth/fastify #9587

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open

Conversation

hillac
Copy link
Contributor

@hillac hillac commented Jan 9, 2024

☕️ Reasoning

This PR is a port of @rexfordessilfie's @auth/express to Fastify. It uses similar translation layer with Web API Request and Response to fastify types. I opted to make it a plugin.

Implementation

  • Introduce a toWebRequest helper that converts an FastifyRequest into a web
  • Introduce a toFastifyReply helper that converts a web Request into an FastifyReply
  • Introduce an FastifyAuth(authConfig) initializer which returns a fastify plugin function of type FastifyPluginAsync to fulfill authentication requests. Under the hood, it:
  • It calls toWebRequest(request) to get a web Request
  • Forwards the web request to Auth (from @auth/core) to get back a web Response
  • Forwards the web response along with Fastify's reply to toFastifyReply(response, reply) to respond to fulfill the request

Tests

  • Tests the toWebRequest and toFastifyReply helpers to ensure that they forward all headers, and body and in the right format (depending on content-type)
  • Tests the full login flow of a credentials provider
  • Tests the getSession by mocking the session

Documentation

I still haven't finished converting the express docs to fastify, just the main example is done so far.

Notes

  • In the docs, I've added the trust proxy for the https issue. I've yet to test if this is actually required in fastify.

  • For the async handlers, I've used return body instead of reply.send(body). I'm not sure whats preferred.

  • The body is unknown type in FastifyRequest so I checked typeof req.body === 'object' && req.body !== null in encodeRequestBody. I'm not sure if this is ok. In order to test encodeUrlEncoded, I had to add checks for the body type.

  • For the response tests, the fastify injector returned the body as a string, so I had to stringify the expected value in the test equality. It also added ; charset=utf-8 to the end of the content type header, so I stripped that off for the test equality. I'm not sure if this is an issue.

  • @fastify/formbody might need to be a peer dependency, I'm not sure.

🧢 Checklist

  • Documentation
  • Tests
  • Ready to be merged

🎫 Affected issues

📌 Resources

Ported express package to fastify and added the integration test for login.
Copy link

vercel bot commented Jan 9, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
auth-docs ❌ Failed (Inspect) May 24, 2024 1:41pm
1 Ignored Deployment
Name Status Preview Comments Updated (UTC)
next-auth-docs ⬜️ Ignored (Inspect) Visit Preview May 24, 2024 1:41pm

Copy link

vercel bot commented Jan 9, 2024

@hillac is attempting to deploy a commit to the authjs Team on Vercel.

A member of the Team first needs to authorize it.

@hillac
Copy link
Contributor Author

hillac commented Jan 9, 2024

@ianschmitz How does this compare to the method you used?

@ianschmitz
Copy link

@ianschmitz How does this compare to the method you used?

I didn't get a chance to take it all the way to production, so I don't think I have a ton of feedback to give

@ndom91
Copy link
Member

ndom91 commented Jan 15, 2024

So first of all, thanks for contributing this! I'm working on a fastify application as we speak and had been putting off integrating Auth.js 😂

Anyway, I did some testing and it seems it's having an issue with esm/cjs and @auth/core. Error message printed when starting:

{"level":50,"time":1705315403580,"pid":1221767,"hostname":"ndo4","err":
{"type":"Error","message":"No \"exports\" main defined in /opt/ndomino/sveltekasten-
rss/node_modules/@auth/core/package.json","stack":"Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: 
No \"exports\" main defined in /opt/ndomino/sveltekasten-
rss/node_modules/@auth/core/package.json\n    at __node_internal_captureLargerStackTrace 
(node:internal/errors:497:5)\n    at new NodeError (node:internal/errors:406:5)\n    at 
exportsNotFound (node:internal/modules/esm/resolve:268:10)\n    at packageExportsResolve 
(node:internal/modules/esm/resolve:542:13)\n    at resolveExports 
(node:internal/modules/cjs/loader:547:36)\n    at Module._findPath 
(node:internal/modules/cjs/loader:621:31)\n    at Module._resolveFilename 
(node:internal/modules/cjs/loader:1034:27)\n    at a._resolveFilename 
(/opt/ndomino/sveltekasten-
rss/node_modules/.pnpm/tsx@4.7.0/node_modules/tsx/dist/cjs/index.cjs:1:1729)\n    at 
Module._load (node:internal/modules/cjs/loader:901:27)\n    at Module.require 
(node:internal/modules/cjs/loader:1115:19)\n    at require 
(node:internal/modules/helpers:130:18)\n   

Looks like its trying to resolve an import via esm/resolve helper, then falling back to cjs loader and then failing.. ./node_modules/@auth/core does contain the full package and normal package.json with the export map as expected, of course theres no main export, but it should pick up the import key 🤔

My fastify application is rather simple/straightforward atm and all ESM as far as I can tell

  • "type": "module" in root package.json
  • using import everywhere
  • Running it with tsx watch src/index.ts

Here's my entrypoint file, I've checked out your branch, built the fastify adapter and pnpm link-ed it into my project to get this:

import Fastify from "fastify"
import { dirname, join } from "path"
import { fileURLToPath } from "url"
import { updateJob } from "@jobs/cron-update"
import autoLoad from "@fastify/autoload"
import formbodyParser from "@fastify/formbody"

import GitHub from "@auth/fastify/providers/github"
import { FastifyAuth } from "@auth/fastify"

const fastify = Fastify({ logger: { level: "warn" } })
const _dirname = dirname(fileURLToPath(import.meta.url))

fastify.register(formbodyParser)

fastify.register(
  FastifyAuth({
    providers: [
      GitHub({
        clientId: process.env.GITHUB_ID,
        clientSecret: process.env.GITHUB_SECRET,
      }),
    ],
  }),
  { prefix: "/api/auth" },
)

fastify.register(autoLoad, {
  dir: join(_dirname, "routes"),
})

fastify.register(autoLoad, {
  dir: join(_dirname, "plugins"),
})
;(async function () {
  const port = process.env.PORT ? parseInt(process.env.PORT) : 8000
  try {
    await fastify.listen({ port, host: "0.0.0.0" })
    console.log(`
  🚀 Server ready at: http://0.0.0.0:${port}
  ⌛ Next cron run at: ${updateJob.nextRun()}
  `)
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})()

Am I missing anything? What did your example / development application look like and how did you run it? Maybe this is just a tsx issue 🤔

@hillac
Copy link
Contributor Author

hillac commented Jan 15, 2024

@ndom91 Here is a demo: https://github.com/hillac/authjs-fastify-demo
I'm not 100% if I've done the auth decorator in the idiomatic way, but it works for me. I'm on node 20.9.0.

Also, as was discussed with @auth/express, it might be nicer for users if the auth decorator is part of @auth/fastify in the plugin. I left it out for now to copy @auth/express.

@ndom91
Copy link
Member

ndom91 commented Jan 15, 2024

@hillac okay great, thanks for the repo example. Turns out I was just having some issues with the fastify autoload plugin. Seems to all work now!

@ndom91
Copy link
Member

ndom91 commented Jan 15, 2024

Regarding your question about the decorator - while I agree, we should try to get as much as possible in the plugin itself, this "authenticate" decorator seems like it would potentially be very different from user to user, no? 🤔

@ndom91
Copy link
Member

ndom91 commented Jan 15, 2024

Also looks like the toFastifyReply behaviour was correct when using an async cb - https://fastify.dev/docs/latest/Reference/Routes/#promise-resolution

packages/frameworks-fastify/src/index.ts Outdated Show resolved Hide resolved
@hillac
Copy link
Contributor Author

hillac commented Mar 26, 2024

Yeah so this is to auto-parse x-www-form-urlencoded bodies correctly, right?

Yep

I figure since this is an authentication library backend, folks will be pointing forms (username / password, or just email address with OAuth provideres, etc.) at it often. So makes sense to me 👍

Yeah, it is definitely needed since the oauth sign in request has to come from a html form in my experience. The redirect fails with an AJAX request.

I made it a peer dependency but thoughts on whether it should just be a normal dependency?

Copy link
Member

ndom91 commented Mar 26, 2024

Yeah I mean if its going to fail in its current state without it installed, I'd say go for it.

What I was implying in my previous comment also is that this doesn't seem like a throw-away dependency, it adds value and seems like it will be useful more than just in this one use-case if folks are integrating form-based sign-in with their Fastify backend, ya know.

@wiput1999
Copy link

I tried to find is there someone are doing integration with Fastify. Finally, found this PR. I'm looking forward for Auth.js support with Fastify.

@Niklas2290
Copy link

  • 1

@Talento90
Copy link

Any updates on this PR?

@ax-at
Copy link

ax-at commented May 11, 2024

Are there any updates on this PR? Looking forward to seeing this move forward to use it in my production apps.

@hillac
Copy link
Contributor Author

hillac commented May 12, 2024

I think it's ready, just waiting for a review I guess.

@ndom91
Copy link
Member

ndom91 commented May 12, 2024

Hey yeah this looks pretty good already! We'd really appreciate some prep in the new docs for Fastify as well

Can you add support for a fastify docs tabs for in the /docs/../Code/index.tsx component? (see: https://github.com/nextauthjs/next-auth/blob/main/docs%2Fcomponents%2FCode%2Findex.tsx).

Also maybe then add fastify example tabs to some of the initial setup docs / code examples in pages like /docs/pages/getting-started/installation.mdx and session-management/*.mdx? Once you add support for the fastify Code tab component (i.e. Code.Fastify), itll be super straight forward to add a fastify Tab in those docs pages

@hillac
Copy link
Contributor Author

hillac commented May 14, 2024

The information in the getting started pages seems kind of redundant to the information already in the api reference page.

Copy link

socket-security bot commented May 24, 2024

New and removed dependencies detected. Learn more about Socket for GitHub ↗︎

Package New capabilities Transitives Size Publisher
npm/@ariakit/react@0.4.6 None +5 4.08 MB ariakit-bot
npm/@auth/express@0.5.5 environment Transitive: network +7 3.85 MB balazsorban
npm/@auth/sveltekit@1.1.0 None +1 144 kB balazsorban
npm/@inkeep/widgets@0.2.278 Transitive: environment, eval, filesystem, network, shell, unsafe +90 33.9 MB sarah-inkeep
npm/@libsql/client@0.6.0 Transitive: environment, filesystem, network, shell +22 63.4 MB penberg
npm/@next/third-parties@14.2.3 None 0 14.1 kB vercel-release-bot
npm/@oddbird/css-anchor-positioning@0.0.5 network +4 4.55 MB jgerigmeyer
npm/@prettier/plugin-pug@3.0.0 environment 0 349 kB shinigami92
npm/@prisma/client@5.14.0 environment, filesystem, shell 0 9 MB prismabot
npm/@radix-ui/react-accordion@1.1.2 None +13 406 kB benoitgrelard
npm/@radix-ui/react-tabs@1.0.4 None +13 376 kB benoitgrelard
npm/@sveltejs/adapter-auto@1.0.0-next.91 environment, filesystem, shell Transitive: eval, network, unsafe +37 221 MB svelte-admin
npm/@sveltejs/kit@2.5.10 environment, eval Transitive: filesystem, network, shell, unsafe +29 221 MB svelte-admin
npm/@types/node@20.12.12 None +1 2.13 MB types
npm/@types/react-dom@18.3.0 None 0 37.8 kB types
npm/@types/react@18.2.78 None 0 433 kB types
npm/@types/react@18.3.3 None +2 1.69 MB types
npm/@vercel/analytics@1.3.0 None 0 0 B
npm/@vercel/kv@1.0.1 environment Transitive: network +1 291 kB vercel-release-bot
npm/express@4.19.2 environment, filesystem, network Transitive: eval, unsafe +63 2.12 MB wesleytodd
npm/morgan@1.10.0 eval Transitive: environment, filesystem, network +8 181 kB dougwilson
npm/next@14.2.3 environment, filesystem, network, shell, unsafe +19 1.2 GB vercel-release-bot
npm/prisma@5.14.0 environment Transitive: eval, filesystem, network, shell +5 12.9 MB prismabot
npm/pug@3.0.2 environment, eval, filesystem +49 6.67 MB pug-bot
npm/react-dom@18.3.1 environment +1 4.61 MB react-bot
npm/react@18.3.1 environment 0 318 kB react-bot
npm/svelte-check@2.10.2 Transitive: environment, filesystem, unsafe +8 21.5 MB svelte-language-tools-deploy
npm/svelte-check@3.6.9 Transitive: environment, eval, filesystem, unsafe +28 10.4 MB svelte-language-tools-deploy
npm/svelte@4.2.15 Transitive: unsafe +13 5.9 MB svelte-admin
npm/svelte@4.2.17 None +2 3.04 MB svelte-admin
npm/tailwindcss@3.4.3 environment, filesystem Transitive: network, shell, unsafe +94 14.5 MB adamwathan
npm/tsx@4.11.0 Transitive: environment, filesystem, network, shell, unsafe +27 225 MB hirokiosame
npm/typescript@5.3.3 None 0 32 MB typescript-bot
npm/typescript@5.4.5 None 0 32.4 MB typescript-bot
npm/unstorage@1.10.2 Transitive: environment, filesystem, network, shell +43 11.7 MB pi0
npm/vite@5.2.10 environment, eval, filesystem, network, shell, unsafe +17 50.4 MB vitebot

🚮 Removed packages: npm/@aws-sdk/client-dynamodb@3.583.0, npm/@aws-sdk/lib-dynamodb@3.583.0, npm/@types/uuid@8.3.4, npm/@typescript-eslint/eslint-plugin@v6.19.1, npm/@typescript-eslint/parser@v6.19.1, npm/classnames@2.5.1, npm/dotenv@10.0.0, npm/nodemailer@6.9.13, npm/supabase@1.169.2, npm/tslib@1.14.1

View full report↗︎

@hillac
Copy link
Contributor Author

hillac commented May 24, 2024

Ok, everything's ready

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants