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

chore(apps): add separate apps/dev/express app #10736

Merged
merged 14 commits into from May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions apps/dev/express/.env.example
@@ -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/express/.gitignore
@@ -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/express/.prettierignore
@@ -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/express/README.md
@@ -0,0 +1,28 @@
> 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).
<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://expressjs.com">Express</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/express">
<img alt="npm" src="https://img.shields.io/npm/v/@auth/express?color=green&label=@auth/express&style=flat-square">
</a>
<a href="https://bundlephobia.com/result?p=@auth/express">
<img src="https://img.shields.io/bundlephobia/minzip/@auth/express?label=size&style=flat-square" alt="Bundle Size"/>
</a>
<a href="https://www.npmtrends.com/@auth/express">
<img src="https://img.shields.io/npm/dm/@auth/express?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

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

export default app
35 changes: 35 additions & 0 deletions apps/dev/express/package.json
@@ -0,0 +1,35 @@
{
"name": "express-auth-app",
"description": "Express + Auth.js Developer app",
"type": "module",
"private": true,
"scripts": {
"start": "node --env-file=.env 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",
"lint": "eslint src/*.ts --fix",
"prettier": "prettier src/*.ts --write"
},
"author": "Auth.js Team (https://authjs.dev/contributors)",
"license": "MIT",
"dependencies": {
"@auth/express": "workspace:*",
"express": "^4.19.2",
"morgan": "^1.10.0",
"pug": "^3.0.2"
},
"devDependencies": {
"@prettier/plugin-pug": "^3.0.0",
"@types/express": "^4.17.21",
"@types/morgan": "^1.9.9",
"@types/pug": "^2.0.10",
"tsx": "^4.7.3",
"typescript": "5.4.5"
},
"engines": {
"node": ">=20.11.0"
}
}
5 changes: 5 additions & 0 deletions apps/dev/express/public/css/style.css
@@ -0,0 +1,5 @@
@tailwind base;

@tailwind components;

@tailwind utilities;
71 changes: 71 additions & 0 deletions apps/dev/express/src/app.ts
@@ -0,0 +1,71 @@
import express, { type Request, type Response } from "express"
import logger from "morgan"
import { join } from "node:path"

import {
errorHandler,
errorNotFoundHandler,
} from "./middleware/error.middleware.js"

import {
authenticatedUser,
currentSession,
} from "./middleware/auth.middleware.js"
import { ExpressAuth } from "@auth/express"
import { authConfig } from "./config/auth.config.js"
import * as pug from "pug"

export const app = express()

app.set("port", process.env.PORT || 3004)

// @ts-expect-error (https://stackoverflow.com/questions/45342307/error-cannot-find-module-pug)
app.engine("pug", pug.__express)
app.set("views", join(import.meta.dirname, "..", "views"))
app.set("view engine", "pug")

// Trust Proxy for Proxies (Heroku, Render.com, Docker behind Nginx, etc)
// https://stackoverflow.com/questions/40459511/in-express-js-req-protocol-is-not-picking-up-https-for-my-secure-link-it-alwa
app.set("trust proxy", true)

app.use(logger("dev"))

// Serve static files
// NB: Uncomment this out if you want Express to serve static files for you vs. using a
// hosting provider which does so for you (for example through a CDN).
// app.use(express.static(join(import.meta.dirname, "..", "public")))

// Parse incoming requests data
app.use(express.urlencoded({ extended: true }))
app.use(express.json())

// Set session in res.locals
app.use(currentSession)

// Set up ExpressAuth to handle authentication
// IMPORTANT: It is highly encouraged set up rate limiting on this route
app.use("/api/auth/*", ExpressAuth(authConfig))

// Routes
app.get("/protected", async (_req: Request, res: Response) => {
res.render("protected", { session: res.locals.session })
})

app.get(
"/api/protected",
authenticatedUser,
async (_req: Request, res: Response) => {
res.json(res.locals.session)
},
)

app.get("/", async (_req: Request, res: Response) => {
res.render("index", {
title: "Express Auth Example",
user: res.locals.session?.user,
})
})

// Error handlers
app.use(errorNotFoundHandler)
app.use(errorHandler)
69 changes: 69 additions & 0 deletions apps/dev/express/src/config/auth.config.ts
@@ -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,
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/express/src/errors.ts
@@ -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"
}
}
29 changes: 29 additions & 0 deletions apps/dev/express/src/middleware/auth.middleware.ts
@@ -0,0 +1,29 @@
import { getSession } from "@auth/express"
import { authConfig } from "../config/auth.config.js"
import type { NextFunction, Request, Response } from "express"

export async function authenticatedUser(
req: Request,
res: Response,
next: NextFunction
) {
const session = res.locals.session ?? (await getSession(req, authConfig))

res.locals.session = session

if (session) {
return next()
}

res.status(400).json({ message: "Not Authenticated" })
}

export async function currentSession(
req: Request,
res: Response,
next: NextFunction
) {
const session = await getSession(req, authConfig)
res.locals.session = session
return next()
}
24 changes: 24 additions & 0 deletions apps/dev/express/src/middleware/error.middleware.ts
@@ -0,0 +1,24 @@
import type { NextFunction, Request, Response } from "express"
import { HttpError, NotFoundError } from "../errors.js"

export const errorHandler = (
err: HttpError | Error,
_req: Request,
res: Response,
_next: NextFunction
): void => {
// Render the error page
res.status(("status" in err && err.status) || 500)
res.render("error", {
title: "status" in err ? err.status : err.name,
message: err.message,
})
}

export const errorNotFoundHandler = (
_req: Request,
_res: Response,
next: NextFunction
): void => {
next(new NotFoundError("Not Found"))
}
9 changes: 9 additions & 0 deletions apps/dev/express/src/server.ts
@@ -0,0 +1,9 @@
import { app } from "./app.js"

const port = app.get("port")

const server = app.listen(port, () => {
console.log(`Listening on port ${port}`)
})

export default server
16 changes: 16 additions & 0 deletions apps/dev/express/tsconfig.json
@@ -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/express/views/error.pug
@@ -0,0 +1,5 @@
extends layout

block content
h1=title
p=message
11 changes: 11 additions & 0 deletions apps/dev/express/views/index.pug
@@ -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.