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: add fastify example #1

Merged
merged 2 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions apps/dev/fastify/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
AUTH_SECRET=

AUTH_GITHUB_ID=
AUTH_GITHUB_SECRET=

AUTH_GOOGLE_ID=
AUTH_GOOGLE_SECRET=
21 changes: 21 additions & 0 deletions apps/dev/fastify/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# API keys and secrets
.env

# Dependency directory
node_modules

# Editors
.idea
*.iml
.vscode/settings.json

# OS metadata
.DS_Store
Thumbs.db

# Ignore built ts files
dist/**/*

# Ignore built css files
/public/css/output.css

14 changes: 14 additions & 0 deletions apps/dev/fastify/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

.DS_Store
node_modules
/dist
/.turbo
/package
.env
.env.*
!.env.example

# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock
28 changes: 28 additions & 0 deletions apps/dev/fastify/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
> The example repository is maintained from a [monorepo](https://github.com/nextauthjs/next-auth/tree/main/apps/examples/fastify). Pull Requests should be opened against [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth).

<p align="center">
<br/>
<a href="https://authjs.dev" target="_blank"><img width="150px" src="https://authjs.dev/img/logo-sm.png" /></a>
<h3 align="center">Auth.js Example App with <a href="https://fastify.dev">Fastify</a></h3>
<p align="center">
Open Source. Full Stack. Own Your Data.
</p>
<p align="center" style="align: center;">
<a href="https://npm.im/@auth/fastify">
<img alt="npm" src="https://img.shields.io/npm/v/@auth/fastify?color=green&label=@auth/fastify&style=flat-square">
</a>
<a href="https://bundlephobia.com/result?p=@auth/fastify">
<img src="https://img.shields.io/bundlephobia/minzip/@auth/fastify?label=size&style=flat-square" alt="Bundle Size"/>
</a>
<a href="https://www.npmtrends.com/@auth/fastify">
<img src="https://img.shields.io/npm/dm/@auth/fastify?label=%20downloads&style=flat-square" alt="Downloads" />
</a>
<a href="https://npm.im/next-auth">
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
</a>
</p>
</p>

# Documentation

- [fastify.authjs.dev](https://fastify.authjs.dev)
3 changes: 3 additions & 0 deletions apps/dev/fastify/api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { app } from "../src/app.js"

export default app
34 changes: 34 additions & 0 deletions apps/dev/fastify/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "fastify-auth-app",
"description": "Fastify + Auth.js Developer app",
"type": "module",
"private": true,
"scripts": {
"start": "node dist/server.js",
"clean": "rm -rf dist",
"build": "pnpm build:ts && pnpm build:css",
"build:ts": "tsc",
"build:css": "tailwindcss -i ./public/css/style.css -o ./public/css/output.css",
"dev": "tsx watch --env-file=.env src/server.ts & pnpm build:css -w",
"debug": "tsx watch --inspect --env-file=.env src/server.ts & pnpm build:css -w",
"lint": "eslint src/*.ts --fix",
"prettier": "prettier src/*.ts --write"
},
"author": "Songkeys (https://github.com/songkeys)",
"license": "MIT",
"dependencies": {
"@auth/fastify": "workspace:*",
"fastify": "^5.0.0",
"@fastify/view": "^10.0.1",
"@fastify/static": "^8.0.1",
"pug": "^3.0.2",
"tailwindcss": "^3.4.3"
},
"devDependencies": {
"@prettier/plugin-pug": "^3.0.0",
"@types/morgan": "^1.9.9",
"@types/pug": "^2.0.10",
"tsx": "^4.7.3",
"typescript": "5.4.5"
}
}
5 changes: 5 additions & 0 deletions apps/dev/fastify/public/css/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@tailwind base;

@tailwind components;

@tailwind utilities;
81 changes: 81 additions & 0 deletions apps/dev/fastify/src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import Fastify from "fastify"

import * as path from "node:path"
import {
errorHandler,
errorNotFoundHandler,
} from "./middleware/error.middleware.js"
import {
authenticatedApi,
authenticatedPage,
currentSession,
} from "./middleware/auth.middleware.js"

import { FastifyAuth } from "@auth/fastify"
import { authConfig } from "./config/auth.config.js"

import * as pug from "pug"
import fastifyView from "@fastify/view"
import fastifyStatic from "@fastify/static"

// Trust Proxy for Proxies (Heroku, Render.com, Docker behind Nginx, etc)
export const fastify = Fastify({ trustProxy: true, logger: true })

// Decorating the reply is not required but will optimise performance
// Only decorate the reply with a value type like null, as reference types like objects are shared among all requests, creating a security risk.
fastify.decorateReply("session", null)

fastify.register(fastifyView, {
engine: {
pug,
},
root: path.join(import.meta.dirname, "..", "views"),
})

// Serve static files
// NB: Uncomment this out if you want Fastify to serve static files for you vs. using a
// hosting provider which does so for you (for example through a CDN).
fastify.register(fastifyStatic, {
root: path.join(import.meta.dirname, "..", "public"),
})

// Set session in reply
fastify.addHook("preHandler", currentSession)

// Set up FastifyAuth to handle authentication
// IMPORTANT: It is highly encouraged set up rate limiting on this route
fastify.register(FastifyAuth(authConfig), { prefix: "/api/auth" })

// Routes
fastify.get(
"/protected",
{ preHandler: [authenticatedPage] },
async (req, reply) => {
return reply.view("protected", { session: reply.session })
}
)

fastify.get(
"/api/protected",
{ preHandler: [authenticatedApi] },
async (req, reply) => {
return reply.send(reply.session)
}
)

fastify.get("/", async (_req, reply) => {
return reply.view("index", {
title: "Fastify Auth Example",
user: reply.session?.user,
})
})

fastify.get("/2", async (_req, reply) => {
return reply.view("index", {
title: "Fastify Auth Example",
user: reply.session?.user,
})
})

fastify.setErrorHandler(errorHandler)
fastify.setNotFoundHandler(errorNotFoundHandler)
69 changes: 69 additions & 0 deletions apps/dev/fastify/src/config/auth.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import Apple from "@auth/express/providers/apple"
import Auth0 from "@auth/express/providers/auth0"
import AzureB2C from "@auth/express/providers/azure-ad-b2c"
import BoxyHQSAML from "@auth/express/providers/boxyhq-saml"
import Cognito from "@auth/express/providers/cognito"
import Coinbase from "@auth/express/providers/coinbase"
import Discord from "@auth/express/providers/discord"
import Dropbox from "@auth/express/providers/dropbox"
import Facebook from "@auth/express/providers/facebook"
import GitHub from "@auth/express/providers/github"
import Gitlab from "@auth/express/providers/gitlab"
import Google from "@auth/express/providers/google"
import Hubspot from "@auth/express/providers/hubspot"
import Keycloak from "@auth/express/providers/keycloak"
import LinkedIn from "@auth/express/providers/linkedin"
import Netlify from "@auth/express/providers/netlify"
import Okta from "@auth/express/providers/okta"
import Passage from "@auth/express/providers/passage"
import Pinterest from "@auth/express/providers/pinterest"
import Reddit from "@auth/express/providers/reddit"
import Slack from "@auth/express/providers/slack"
import Spotify from "@auth/express/providers/spotify"
import Twitch from "@auth/express/providers/twitch"
import Twitter from "@auth/express/providers/twitter"
import WorkOS from "@auth/express/providers/workos"
import Zoom from "@auth/express/providers/zoom"

export const authConfig = {
trustHost: true,
debug: process.env.NODE_ENV !== "production" ? true : false,
songkeys marked this conversation as resolved.
Show resolved Hide resolved
providers: [
Apple,
Auth0,
AzureB2C({
clientId: process.env.AUTH_AZURE_AD_B2C_ID,
clientSecret: process.env.AUTH_AZURE_AD_B2C_SECRET,
issuer: process.env.AUTH_AZURE_AD_B2C_ISSUER,
}),
BoxyHQSAML({
clientId: "dummy",
clientSecret: "dummy",
issuer: process.env.AUTH_BOXYHQ_SAML_ISSUER,
}),
Cognito,
Coinbase,
Discord,
Dropbox,
Facebook,
GitHub,
Gitlab,
Google,
Hubspot,
Keycloak,
LinkedIn,
Netlify,
Okta,
Passage,
Pinterest,
Reddit,
Slack,
Spotify,
Twitch,
Twitter,
WorkOS({
connection: process.env.AUTH_WORKOS_CONNECTION!,
}),
Zoom,
],
}
14 changes: 14 additions & 0 deletions apps/dev/fastify/src/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export class HttpError extends Error {
status: number
constructor(status: number, message: string) {
super(message)
this.status = status
}
}

export class NotFoundError extends HttpError {
constructor(message: string, status = 404) {
super(status, message)
this.name = "NotFoundError"
}
}
28 changes: 28 additions & 0 deletions apps/dev/fastify/src/middleware/auth.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//// @ts-nocheck
import { getSession } from "@auth/fastify"
import { authConfig } from "../config/auth.config.js"
import { FastifyReply, FastifyRequest } from "fastify"

export async function authenticatedApi(
req: FastifyRequest,
reply: FastifyReply
) {
reply.session ??= (await getSession(req, authConfig)) ?? null
if (!reply.session) {
reply.status(401).send({ message: "Not Authenticated" })
}
}

export async function authenticatedPage(
req: FastifyRequest,
reply: FastifyReply
) {
reply.session ??= (await getSession(req, authConfig)) ?? null
if (!reply.session) {
return reply.view("unauthenticated")
}
}

export async function currentSession(req: FastifyRequest, reply: FastifyReply) {
reply.session = (await getSession(req, authConfig)) ?? null
}
22 changes: 22 additions & 0 deletions apps/dev/fastify/src/middleware/error.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//// @ts-nocheck
import { HttpError, NotFoundError } from "../errors.js"
import { FastifyReply, FastifyRequest } from "fastify"

export const errorHandler = (
err: HttpError | Error,
_req: FastifyRequest,
_reply: FastifyReply
): void => {
const statusCode = (err instanceof HttpError && err.status) || 500
_reply.status(statusCode).view("error", {
title: err instanceof HttpError ? err.status : err.name,
message: err.message,
})
}

export const errorNotFoundHandler = (
_req: FastifyRequest,
_reply: FastifyReply
): void => {
new NotFoundError("Not Found")
}
18 changes: 18 additions & 0 deletions apps/dev/fastify/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const { fastify } = await import("./app.js")

// const address = fastify.server.address()
// const port = (typeof address === "object" && address?.port) || 3000

const port = Number(process.env.PORT) || 3000

const start = async () => {
try {
await fastify.listen({ port })
fastify.log.info(`server listening on ${fastify.server.address()}`)
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
}

start()
16 changes: 16 additions & 0 deletions apps/dev/fastify/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"module": "NodeNext",
"esModuleInterop": true,
"target": "esnext",
"noImplicitAny": true,
"moduleResolution": "NodeNext",
"sourceMap": true,
"outDir": "dist",
"baseUrl": ".",
"skipLibCheck": true,
"strict": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}
5 changes: 5 additions & 0 deletions apps/dev/fastify/views/error.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
extends layout

block content
h1=title
p=message
11 changes: 11 additions & 0 deletions apps/dev/fastify/views/index.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
extends layout

block content
h1=title
p
| This is an example site to demonstrate how to use #{ ' ' }
a(href="https://expressjs.com/") Express
| #{ ' ' } with #{ ' ' }
a(href="https://authjs.dev/reference/express") Express Auth
|
| for authentication.
Loading
Loading