Compare commits

..

75 Commits

Author SHA1 Message Date
Thang Vu
3d8350c676 add timeout for test step 2022-12-20 11:45:56 +07:00
Balázs Orbán
6fdcaf6ebd update lock file 2022-12-19 04:35:15 +01:00
Balázs Orbán
d2f0ac446b Merge branch 'main' into chore/clean-dev-deps 2022-12-19 04:34:23 +01:00
Thang Vu
2c5244e67e feat: Enable turbo remote caching for GH Actions 2022-12-19 10:27:13 +07:00
Balázs Orbán
f7275c7527 chore: type updates
fix import

chore: more docs update
2022-12-19 04:03:03 +01:00
Balázs Orbán
e699ff14b8 docs: /reference sidebar improvements (#6115)
* wip

* 🤖 lazy commit

* 🤖 lazy commit

* revert some changes, remove prettier jsdoc plugin for now

* sidebar tweaks

* add adapter module docs

* remove provider docs

* embed all reflections under modules

Based on: https://github.com/TypeStrong/typedoc/issues/2006

Related: https://github.com/tgreyuk/typedoc-plugin-markdown/issues/338

* no trailing slash, update theme

* updates

* update snapshot

* update sidebar and overview
2022-12-19 01:00:06 +00:00
ndom91
6eab7ac25f chore(actions): revert var dump 2022-12-19 01:31:11 +01:00
ndom91
9b05dbc540 chore(actions): add path 2022-12-19 01:28:33 +01:00
ndom91
132a76d951 chore(actions): test github context 2022-12-19 01:26:54 +01:00
ndom91
66cbb522d9 Merge branch 'main' of ssh://github.com/nextauthjs/next-auth 2022-12-19 01:12:47 +01:00
ndom91
553924d902 chore(actions): fix validator path 2022-12-19 01:12:38 +01:00
Balázs Orbán
cba81f0b8c chore: add snippets 2022-12-18 14:12:56 +01:00
Balázs Orbán
b7171ab790 chore: tweak jsdoc formatting/linting 2022-12-18 14:06:00 +01:00
ndom91
43c8f663c6 Revert "fix: pnpm-lock.yaml"
This reverts commit b16b048991.
2022-12-18 04:06:37 +01:00
ndom91
b16b048991 fix: pnpm-lock.yaml 2022-12-18 04:05:16 +01:00
ndom91
62a5d70f9b Merge branch 'main' of ssh://github.com/nextauthjs/next-auth 2022-12-18 04:04:46 +01:00
Balázs Orbán
1b671ae83d docs: format landing page 2022-12-18 02:19:07 +00:00
Andrew Manzanero
cc4b9fc2fc fix(core): check for oidc account types in callback-handler.ts (#6108)
* check oidc account types

* Apply suggestions from code review

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2022-12-18 01:46:33 +00:00
Nico Domino
4935166372 chore(docs): add provider-issue and github-discussions redirects (#6110) 2022-12-18 01:44:37 +00:00
Balázs Orbán
695f937dbd chore: change sync action comment 2022-12-18 01:18:08 +00:00
Balázs Orbán
c527801875 remove empty test, move configs 2022-12-18 01:42:17 +01:00
Balázs Orbán
20ba5e1fcd run formatter, include docs 2022-12-18 01:10:53 +01:00
Balázs Orbán
01d88733c3 don't format next-auth 2022-12-18 01:07:18 +01:00
Balázs Orbán
f0720cbbd8 move eslint and prettier configs to top 2022-12-18 01:05:31 +01:00
ndom91
ad9eec3676 chore(docs): add provider-issue and github-discussions redirects 2022-12-18 00:07:41 +01:00
Balázs Orbán
a264638274 migrate turbo config 2022-12-18 00:00:26 +01:00
Balázs Orbán
663c5ef23b chore: upgrade workspace dependencies 2022-12-18 00:00:18 +01:00
Balázs Orbán
2e924edcdf chore: stop sync next-auth-example from main branch 2022-12-17 19:40:20 +00:00
Balázs Orbán
c7627778eb chore: no prettier check yet 2022-12-17 14:33:52 +01:00
Balázs Orbán
8b5644453b docs: set up API reference generation 2022-12-17 14:26:14 +01:00
Balázs Orbán
84291d3e81 chore: fix formatting and linting 2022-12-16 15:57:55 +01:00
Nico Domino
67e5c236f6 chore(docs): wildcard path matching (#6092) 2022-12-16 14:22:51 +01:00
Nico Domino
8972defa4b chore(vercel): add errors and warnings subdomain redirects (#6091) 2022-12-16 14:10:39 +01:00
Aleš Vaupotič
85667dd681 docs(sveltekit): add note about deploying to providers other than vercel (#6075)
* Directions to deploy outside Vercel

An additional ENV variable is needed when deploying with another service.

* Updated as suggested, AUTH_TRUST_HOST is a boolean

Add AUTH_TRUST_HOST for deploy outside Vercel
2022-12-16 05:06:26 +01:00
Lluis Agusti
d9532745eb docs: update names to Auth.js (#6077) 2022-12-16 05:03:31 +01:00
Robert Soriano
1e6daa8304 chore(examples): use @auth/core in Nuxt playground (#6081)
* nuxt: rewrite server handler to use @auth/core

* nuxt: fix main tsconfig

* nuxt: remove unused module

* nuxt: update client and server ports

* nuxt: remove iframe style

* nuxt: update readme
2022-12-16 02:47:44 +00:00
meesvandongen
70a3e3f662 chore(docs): Fix nextjs.md code block (#6037)
chore: fix quotes
2022-12-16 01:00:22 +01:00
Balázs Orbán
875f79d11e chore(release): bump package version(s) [skip ci] 2022-12-15 11:38:20 +01:00
Shivam Meena
6cfe502ae0 fix(sveltekit): correctly reference SvelteKit as peer dependency (#6066)
You added svelte-kit@^1.0.0  instead of @sveltejs/kit": "^1.0.0

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2022-12-15 10:28:55 +00:00
Robert Soriano
91c6b05ed8 fix(sveltekit): reuse types from @auth/core/providers (#6064)
* feat: use client provider types from @auth/core/providers

* format

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2022-12-15 10:27:32 +00:00
Nico Domino
45a18930c8 chore: try redirect instead of rewrite (#6063) 2022-12-15 00:34:24 +01:00
Arnaud Zheng
6f22a49c7d chore: removing console log (#6061) 2022-12-15 00:24:15 +01:00
Nico Domino
fea30069c9 chore: try redirect instead of rewrite (#6062) 2022-12-15 00:14:42 +01:00
Nico Domino
cd01707530 chore: try redirect instead of rewrite (#6060) 2022-12-14 23:56:49 +01:00
Lluis Agusti
d9a2df3a3d chore(docs): fix broken links (#6053) 2022-12-14 21:49:15 +01:00
Nico Domino
f4a1ed1eb7 chore(docs): add note regarding AUTH_SECRET to SvelteKit docs (#6058) 2022-12-14 21:30:54 +01:00
Nico Domino
a97737cc18 fix: rename social-media-card.png for cache busting (#6057) 2022-12-14 20:43:17 +01:00
Nico Domino
b44d1a005e fix: update docusaurus config url to authjs.dev (#6056) 2022-12-14 20:34:41 +01:00
Nico Domino
2c077e1491 chore(docs): add flex-wrap to docs home button wrapper (#6055) 2022-12-14 20:28:05 +01:00
Nico Domino
19804661d2 chore(docs): fix social media card copy (#6054) 2022-12-14 19:16:16 +01:00
Nico Domino
b7f1e3e7f8 docs: move to Auth.js (#6024)
Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Lluis Agusti <hi@llu.lu>
Co-authored-by: Thang Vu <hi@thvu.dev>
2022-12-14 18:33:49 +01:00
Balázs Orbán
7757024d79 chore(examples): update example readmes 2022-12-14 17:13:48 +01:00
Balázs Orbán
3f15dc67e1 chore: update readme 2022-12-14 16:59:08 +01:00
Balázs Orbán
5359694b8f chore: change package versions to latest 2022-12-14 16:45:46 +01:00
Balázs Orbán
66686fa5fc chore(example): rename repo 2022-12-14 16:41:14 +01:00
Balázs Orbán
1d6330b719 chore: empty commit 2022-12-14 16:40:38 +01:00
Balázs Orbán
0eb20d1097 chore: sync SvelteKit Auth example 2022-12-14 16:39:21 +01:00
Balázs Orbán
ac30402c6a chore(examples): move examples 2022-12-14 15:58:45 +01:00
Balázs Orbán
caa6c6ae42 chore(release): bump package version(s) [skip ci] 2022-12-14 15:37:29 +01:00
Nico Domino
a6ac48314e chore(actions): issue-validator path to 'repro.md' (#6051) 2022-12-14 15:36:30 +01:00
Balázs Orbán
f8675bc245 fix(sveltekit): add svelte as peer dependency, fix env vars 2022-12-14 15:32:19 +01:00
Balázs Orbán
3d4842dcc9 fix(core): change imports 2022-12-14 15:31:57 +01:00
Balázs Orbán
7d7d1b2f80 chore(release): bump package version(s) [skip ci] 2022-12-14 13:27:31 +01:00
Balázs Orbán
9a4f3db7b0 chore: format 2022-12-14 13:10:13 +01:00
Balázs Orbán
6aad07a95c chore: update lock file 2022-12-14 13:10:13 +01:00
Balázs Orbán
cfed5b976f fix(sveltekit): include module augmentation 2022-12-14 13:10:13 +01:00
Balázs Orbán
d34108091f fix(core): use preact as JSX runtime 2022-12-14 13:10:13 +01:00
Balázs Orbán
7bf79b89a8 chore(sveltekit): clean up playground 2022-12-14 13:10:13 +01:00
Balázs Orbán
4cd688703a fix(core): drop "in production" from missing secret error 2022-12-14 13:10:13 +01:00
Balázs Orbán
57b176840e chore(release): bump package version(s) [skip ci] 2022-12-14 09:49:43 +01:00
Thang Vu
6298d955df fix(frameworks): run check before building for @auth/sveltekit (#6044)
* fix(frameworks): run check before building for @auth/sveltekit

* run format
2022-12-14 15:44:08 +07:00
Balázs Orbán
2ad1cb3f8c chore(release): bump package version(s) [skip ci] 2022-12-14 02:51:15 +01:00
Balázs Orbán
98707282eb fix(release): tweak package metadata 2022-12-14 02:45:57 +01:00
Balázs Orbán
f4a2430891 fix(release): build packages before publish 2022-12-14 02:45:18 +01:00
Balázs Orbán
575bcb5710 chore: format sveltekit playground 2022-12-13 23:45:32 +01:00
597 changed files with 22019 additions and 22944 deletions

12
.eslintignore Normal file
View File

@@ -0,0 +1,12 @@
../core/adapters.*
../core/index.*
../core/jwt
../core/lib
../core/providers
.gitignore
../frameworks-sveltekit/*.cjs
../frameworks-sveltekit/client.*
../frameworks-sveltekit/index.*
../frameworks-sveltekit/tests
../frameworks-sveltekit/.svelte-kit

View File

@@ -1,20 +1,23 @@
// @ts-check
const path = require("path")
/** @type {import("eslint").ESLint.ConfigData} */
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
extends: ["standard-with-typescript", "prettier"],
rules: {
camelcase: "off",
"@typescript-eslint/naming-convention": "off",
"@typescript-eslint/strict-boolean-expressions": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/triple-slash-reference": "off",
"@typescript-eslint/promise-function-async": "off",
},
overrides: [
{
files: ["*.ts", "*.tsx"],
extends: ["standard-with-typescript", "prettier"],
rules: {
camelcase: "off",
"@typescript-eslint/naming-convention": "off",
"@typescript-eslint/strict-boolean-expressions": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/restrict-template-expressions": "off",
},
parserOptions: {
project: [
path.resolve(__dirname, "./packages/**/tsconfig.eslint.json"),
@@ -23,19 +26,57 @@ module.exports = {
],
},
},
{
files: ["*.test.ts", "*.test.js"],
env: { jest: true },
},
{
files: ["docs"],
plugins: ["@docusaurus"],
extends: ["plugin:@docusaurus/recommended"],
},
{
files: ["packages/core/src/**/*"],
plugins: ["jsdoc"],
extends: ["plugin:jsdoc/recommended"],
rules: {
"jsdoc/require-param": "off",
"jsdoc/require-returns": "off",
"jsdoc/require-jsdoc": [
"warn",
{ publicOnly: true, enableFixer: false },
],
"jsdoc/no-multi-asterisks": ["warn", { allowWhitespace: true }],
"jsdoc/tag-lines": "off",
},
},
{
files: ["packages/core/src/adapters.ts"],
rules: {
"@typescript-eslint/method-signature-style": "off",
},
},
{
files: ["packages/frameworks-sveltekit/**/*"],
plugins: ["svelte3", "@typescript-eslint"],
parserOptions: {
sourceType: "module",
ecmaVersion: 2020,
},
env: {
browser: true,
es2017: true,
node: true,
},
},
],
extends: ["prettier"],
globals: {
localStorage: "readonly",
location: "readonly",
fetch: "readonly",
},
rules: {
camelcase: "off",
},
plugins: ["jest"],
env: {
"jest/globals": true,
},
ignorePatterns: [".eslintrc.js"],
ignorePatterns: [
"**/dist/**",
"**/node_modules/**",
".eslintrc.js",
"**/.turbo/**",
"**/coverage/**",
"**/build/**",
],
}

View File

@@ -4,11 +4,8 @@ import * as github from "@actions/github"
// @ts-expect-error
import * as core from "@actions/core"
import { readFileSync } from "node:fs"
import { join } from "node:path"
const addReproductionLabel = "incomplete"
const __dirname =
"/home/runner/work/nextauthjs/next-auth/.github/actions/issue-validator"
/**
* @typedef {{
@@ -73,7 +70,7 @@ async function run() {
}),
client.issues.createComment({
...issueCommon,
body: readFileSync(join(__dirname, "repro.md"), "utf8"),
body: readFileSync("repro.md", "utf8"),
}),
])
return core.info(

View File

@@ -54,7 +54,7 @@ upstash-redis:
xata:
- packages/adapter-xata/**
legacy:
core:
- packages/next-auth/src/**/*
style:

15
.github/sync.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
# Note that nextauthjs/next-auth-example syncs from the v4 branch
nextauthjs/sveltekit-auth-example:
- source: apps/example-sveltekit
dest: .
deleteOrphaned: true
- .github/FUNDING.yml
- LICENSE
nextauthjs/next-auth-gatsby-example:
- source: apps/playground-gatsby
dest: .
deleteOrphaned: true
- .github/FUNDING.yml
- LICENSE

View File

@@ -11,7 +11,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 18
- name: 'Run issue validator'
run: node ./.github/actions/issue-validator/index.mjs
- name: "Run issue validator"
run: node /home/runner/work/next-auth/next-auth/.github/actions/issue-validator/index.mjs
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -7,7 +7,6 @@ on:
- "beta"
- "next"
- "3.x"
- "v4"
pull_request:
jobs:
@@ -32,16 +31,14 @@ jobs:
run: pnpm install
- name: Build
run: pnpm build
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
- name: Run tests
run: pnpm test
timeout-minutes: 15
env:
UPSTASH_REDIS_URL: ${{ secrets.UPSTASH_REDIS_URL }}
UPSTASH_REDIS_KEY: ${{ secrets.UPSTASH_REDIS_KEY }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
# - name: Coverage
# uses: codecov/codecov-action@v1
# with:

18
.github/workflows/sync-examples.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Sync Example Repositories
on:
push:
branches:
- main
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Run GitHub File Sync
# Can update to v1 when https://github.com/BetaHuhn/repo-file-sync-action/issues/168 is resolved
uses: BetaHuhn/repo-file-sync-action@v1.16.5
with:
GH_PAT: ${{ secrets.SYNC_EXAMPLE_PAT }}
SKIP_PR: true

12
.gitignore vendored
View File

@@ -34,9 +34,13 @@ packages/next-auth/utils
packages/next-auth/core
packages/next-auth/jwt
packages/next-auth/react
packages/next-auth/*.d.ts*
packages/next-auth/*.js
packages/next-auth/adapters.d.ts
packages/next-auth/adapters.js
packages/next-auth/index.d.ts
packages/next-auth/index.js
packages/next-auth/next
packages/next-auth/middleware.d.ts
packages/next-auth/middleware.js
# Development app
apps/dev/src/css
@@ -47,7 +51,7 @@ apps/dev/typeorm
/.vs/slnx.sqlite-journal
/.vs/slnx.sqlite
/.vs
.vscode
.vscode/generated*
# Jetbrains
.idea
@@ -82,6 +86,8 @@ packages/core/index.*
packages/core/jwt
packages/core/lib
packages/core/providers
packages/core/docs
docs/docs/reference/03-core
# SvelteKit

25
.prettierignore Normal file
View File

@@ -0,0 +1,25 @@
.DS_Store
node_modules
.turbo
# apps/example-* should have their standalonw config
# as they might be cloned via a template like https://github.com/nextauthjs/next-auth-example
# Note: The root is inside the package
# packages
dist
# docs
.docusaurus
build
static
# @auth/core
**/lib/styles/index.ts
**/providers/oauth-types.ts
# @auth/sveltekit
package
index.*
client.*
.svelte-kit

15
.prettierrc.js Normal file
View File

@@ -0,0 +1,15 @@
// @ts-check
/** @type {import("prettier").Config} */
module.exports = {
semi: false,
singleQuote: false,
overrides: [
{
files: "apps/dev/pages/api/auth/[...nextauth].ts",
options: {
printWidth: 150,
},
},
],
}

8
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"files.exclude": {
"packages/core/{jwt,lib,providers,*.js,*.d.ts*}": true,
"packages/next-auth/{client,core,css,jwt,next,providers,react,utils,*.js,*.d.ts}": true
},
"typescript.tsdk": "node_modules/typescript/lib",
"openInGitHub.remote.branch": "main",
}

18
.vscode/snippets.code-snippets vendored Normal file
View File

@@ -0,0 +1,18 @@
{
"oauth2-spec": {
"description": "Markdown link to OAuth 2 specification",
"scope": "typescript",
"prefix": "oauth2",
"body": [
"[OAuth 2](https://datatracker.ietf.org/doc/html/rfc6749)"
]
},
"oidc-spec": {
"description": "Markdown link to OpenID Connect specification",
"scope": "typescript",
"prefix": "oidc",
"body": [
"[OIDC](https://openid.net/specs/openid-connect-core-1_0.html)"
]
},
}

4
apps/dev/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"typescript.tsdk": "../../node_modules/.pnpm/typescript@4.8.4/node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}

View File

@@ -1,220 +0,0 @@
import NextAuth, { type NextAuthOptions } from "next-auth"
// import { NextRequest } from "next/server"
// Providers
import Apple from "next-auth/providers/apple"
import Auth0 from "next-auth/providers/auth0"
import AzureAD from "next-auth/providers/azure-ad"
import AzureB2C from "next-auth/providers/azure-ad-b2c"
import BoxyHQSAML from "next-auth/providers/boxyhq-saml"
// import Cognito from "next-auth/providers/cognito"
import Credentials from "next-auth/providers/credentials"
import Discord from "next-auth/providers/discord"
import DuendeIDS6 from "next-auth/providers/duende-identity-server6"
// import Email from "next-auth/providers/email"
import Facebook from "next-auth/providers/facebook"
import Foursquare from "next-auth/providers/foursquare"
import Freshbooks from "next-auth/providers/freshbooks"
import GitHub from "next-auth/providers/github"
import Gitlab from "next-auth/providers/gitlab"
import Google from "next-auth/providers/google"
// import IDS4 from "next-auth/providers/identity-server4"
import Instagram from "next-auth/providers/instagram"
// import Keycloak from "next-auth/providers/keycloak"
import Line from "next-auth/providers/line"
import LinkedIn from "next-auth/providers/linkedin"
import Mailchimp from "next-auth/providers/mailchimp"
// import Okta from "next-auth/providers/okta"
import Osu from "next-auth/providers/osu"
import Patreon from "next-auth/providers/patreon"
import Slack from "next-auth/providers/slack"
import Spotify from "next-auth/providers/spotify"
import Trakt from "next-auth/providers/trakt"
import Twitch from "next-auth/providers/twitch"
import Twitter from "next-auth/providers/twitter"
import Vk from "next-auth/providers/vk"
import Wikimedia from "next-auth/providers/wikimedia"
import WorkOS from "next-auth/providers/workos"
// // Prisma
// import { PrismaClient } from "@prisma/client"
// import { PrismaAdapter } from "@next-auth/prisma-adapter"
// const client = globalThis.prisma || new PrismaClient()
// if (process.env.NODE_ENV !== "production") globalThis.prisma = client
// const adapter = PrismaAdapter(client)
// // Fauna
// import { Client as FaunaClient } from "faunadb"
// import { FaunaAdapter } from "@next-auth/fauna-adapter"
// const opts = { secret: process.env.FAUNA_SECRET, domain: process.env.FAUNA_DOMAIN }
// const client = globalThis.fauna || new FaunaClient(opts)
// if (process.env.NODE_ENV !== "production") globalThis.fauna = client
// const adapter = FaunaAdapter(client)
// // TypeORM
// import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
// const adapter = TypeORMLegacyAdapter({
// type: "sqlite",
// name: "next-auth-test-memory",
// database: "./typeorm/dev.db",
// synchronize: true,
// })
// // Supabase
// import { SupabaseAdapter } from "@next-auth/supabase-adapter"
// const adapter = SupabaseAdapter({
// url: process.env.NEXT_PUBLIC_SUPABASE_URL,
// secret: process.env.SUPABASE_SERVICE_ROLE_KEY,
// })
export const authOptions: NextAuthOptions = {
// adapter,
// debug: process.env.NODE_ENV !== "production",
theme: {
logo: "https://next-auth.js.org/img/logo/logo-sm.png",
brandColor: "#1786fb",
},
providers: [
Credentials({
credentials: { password: { label: "Password", type: "password" } },
async authorize(credentials) {
if (credentials.password !== "pw") return null
return {
name: "Fill Murray",
email: "bill@fillmurray.com",
image: "https://www.fillmurray.com/64/64",
id: "1",
foo: "",
}
},
}),
Apple({
clientId: process.env.APPLE_ID,
clientSecret: process.env.APPLE_SECRET,
}),
Auth0({
clientId: process.env.AUTH0_ID,
clientSecret: process.env.AUTH0_SECRET,
issuer: process.env.AUTH0_ISSUER,
}),
AzureAD({
clientId: process.env.AZURE_AD_CLIENT_ID,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
tenantId: process.env.AZURE_AD_TENANT_ID,
}),
AzureB2C({
clientId: process.env.AZURE_B2C_ID,
clientSecret: process.env.AZURE_B2C_SECRET,
issuer: process.env.AZURE_B2C_ISSUER,
}),
BoxyHQSAML({
issuer: "https://jackson-demo.boxyhq.com",
clientId: "tenant=boxyhq.com&product=saml-demo.boxyhq.com",
clientSecret: "dummy",
}),
// Cognito({ clientId: process.env.COGNITO_ID, clientSecret: process.env.COGNITO_SECRET, issuer: process.env.COGNITO_ISSUER }),
Discord({
clientId: process.env.DISCORD_ID,
clientSecret: process.env.DISCORD_SECRET,
}),
DuendeIDS6({
clientId: "interactive.confidential",
clientSecret: "secret",
issuer: "https://demo.duendesoftware.com",
}),
Facebook({
clientId: process.env.FACEBOOK_ID,
clientSecret: process.env.FACEBOOK_SECRET,
}),
Foursquare({
clientId: process.env.FOURSQUARE_ID,
clientSecret: process.env.FOURSQUARE_SECRET,
}),
Freshbooks({
clientId: process.env.FRESHBOOKS_ID,
clientSecret: process.env.FRESHBOOKS_SECRET,
}),
GitHub({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
Gitlab({
clientId: process.env.GITLAB_ID,
clientSecret: process.env.GITLAB_SECRET,
}),
Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
// IDS4({ clientId: process.env.IDS4_ID, clientSecret: process.env.IDS4_SECRET, issuer: process.env.IDS4_ISSUER }),
Instagram({
clientId: process.env.INSTAGRAM_ID,
clientSecret: process.env.INSTAGRAM_SECRET,
}),
// Keycloak({ clientId: process.env.KEYCLOAK_ID, clientSecret: process.env.KEYCLOAK_SECRET, issuer: process.env.KEYCLOAK_ISSUER }),
Line({
clientId: process.env.LINE_ID,
clientSecret: process.env.LINE_SECRET,
}),
LinkedIn({
clientId: process.env.LINKEDIN_ID,
clientSecret: process.env.LINKEDIN_SECRET,
}),
Mailchimp({
clientId: process.env.MAILCHIMP_ID,
clientSecret: process.env.MAILCHIMP_SECRET,
}),
// Okta({ clientId: process.env.OKTA_ID, clientSecret: process.env.OKTA_SECRET, issuer: process.env.OKTA_ISSUER }),
Osu({
clientId: process.env.OSU_CLIENT_ID,
clientSecret: process.env.OSU_CLIENT_SECRET,
}),
Patreon({
clientId: process.env.PATREON_ID,
clientSecret: process.env.PATREON_SECRET,
}),
Slack({
clientId: process.env.SLACK_ID,
clientSecret: process.env.SLACK_SECRET,
}),
Spotify({
clientId: process.env.SPOTIFY_ID,
clientSecret: process.env.SPOTIFY_SECRET,
}),
Trakt({
clientId: process.env.TRAKT_ID,
clientSecret: process.env.TRAKT_SECRET,
}),
Twitch({
clientId: process.env.TWITCH_ID,
clientSecret: process.env.TWITCH_SECRET,
}),
Twitter({
clientId: process.env.TWITTER_ID,
clientSecret: process.env.TWITTER_SECRET,
}),
// TwitterLegacy({ clientId: process.env.TWITTER_LEGACY_ID, clientSecret: process.env.TWITTER_LEGACY_SECRET }),
Vk({ clientId: process.env.VK_ID, clientSecret: process.env.VK_SECRET }),
Wikimedia({
clientId: process.env.WIKIMEDIA_ID,
clientSecret: process.env.WIKIMEDIA_SECRET,
}),
WorkOS({
clientId: process.env.WORKOS_ID,
clientSecret: process.env.WORKOS_SECRET,
}),
],
}
/**
* Advanced Initialization - route handler
*/
// const handler = async (
// req: NextRequest,
// routeContext: { params: { nextauth: string[] } }
// ): Promise<any> => {
// return NextAuth(req, routeContext, authOptions)
// }
const handler = NextAuth(authOptions)
export { handler as GET, handler as POST }

View File

@@ -1,6 +1,6 @@
import { getServerSession } from "next-auth/next"
import { unstable_getServerSession } from "next-auth/next"
export default async function Page() {
const session = await getServerSession()
const session = await unstable_getServerSession()
return <pre>{JSON.stringify(session, null, 2)}</pre>
}

View File

@@ -1,6 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@@ -6,7 +6,6 @@
"scripts": {
"clean": "rm -rf .next",
"dev": "next dev",
"lint": "next lint",
"build": "next build",
"start": "next start",
"email": "fake-smtp-server",
@@ -21,15 +20,16 @@
"@prisma/client": "^3",
"@supabase/supabase-js": "^2.0.5",
"faunadb": "^4",
"next": "13.4.12",
"next": "13.0.6",
"next-auth": "workspace:*",
"@auth/core": "workspace:*",
"nodemailer": "^6",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@types/jsonwebtoken": "^8.5.5",
"@types/react": "^18.0.37",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"fake-smtp-server": "^0.8.0",
"pg": "^8.7.3",

View File

@@ -1,39 +1,39 @@
import NextAuth, { NextAuthOptions } from "next-auth"
import { AuthHandler, type AuthOptions } from "@auth/core"
// Providers
import Apple from "next-auth/providers/apple"
import Auth0 from "next-auth/providers/auth0"
import AzureAD from "next-auth/providers/azure-ad"
import AzureB2C from "next-auth/providers/azure-ad-b2c"
import BoxyHQSAML from "next-auth/providers/boxyhq-saml"
// import Cognito from "next-auth/providers/cognito"
import Credentials from "next-auth/providers/credentials"
import Discord from "next-auth/providers/discord"
import DuendeIDS6 from "next-auth/providers/duende-identity-server6"
// import Email from "next-auth/providers/email"
import Facebook from "next-auth/providers/facebook"
import Foursquare from "next-auth/providers/foursquare"
import Freshbooks from "next-auth/providers/freshbooks"
import GitHub from "next-auth/providers/github"
import Gitlab from "next-auth/providers/gitlab"
import Google from "next-auth/providers/google"
// import IDS4 from "next-auth/providers/identity-server4"
import Instagram from "next-auth/providers/instagram"
// import Keycloak from "next-auth/providers/keycloak"
import Line from "next-auth/providers/line"
import LinkedIn from "next-auth/providers/linkedin"
import Mailchimp from "next-auth/providers/mailchimp"
// import Okta from "next-auth/providers/okta"
import Osu from "next-auth/providers/osu"
import Patreon from "next-auth/providers/patreon"
import Slack from "next-auth/providers/slack"
import Spotify from "next-auth/providers/spotify"
import Trakt from "next-auth/providers/trakt"
import Twitch from "next-auth/providers/twitch"
import Twitter from "next-auth/providers/twitter"
import Vk from "next-auth/providers/vk"
import Wikimedia from "next-auth/providers/wikimedia"
import WorkOS from "next-auth/providers/workos"
import Apple from "@auth/core/providers/apple"
import Auth0 from "@auth/core/providers/auth0"
import AzureAD from "@auth/core/providers/azure-ad"
import AzureB2C from "@auth/core/providers/azure-ad-b2c"
import BoxyHQSAML from "@auth/core/providers/boxyhq-saml"
// import Cognito from "@auth/core/providers/cognito"
import Credentials from "@auth/core/providers/credentials"
import Discord from "@auth/core/providers/discord"
import DuendeIDS6 from "@auth/core/providers/duende-identity-server6"
// import Email from "@auth/core/providers/email"
import Facebook from "@auth/core/providers/facebook"
import Foursquare from "@auth/core/providers/foursquare"
import Freshbooks from "@auth/core/providers/freshbooks"
import GitHub from "@auth/core/providers/github"
import Gitlab from "@auth/core/providers/gitlab"
import Google from "@auth/core/providers/google"
// import IDS4 from "@auth/core/providers/identity-server4"
import Instagram from "@auth/core/providers/instagram"
// import Keycloak from "@auth/core/providers/keycloak"
import Line from "@auth/core/providers/line"
import LinkedIn from "@auth/core/providers/linkedin"
import Mailchimp from "@auth/core/providers/mailchimp"
// import Okta from "@auth/core/providers/okta"
import Osu from "@auth/core/providers/osu"
import Patreon from "@auth/core/providers/patreon"
import Slack from "@auth/core/providers/slack"
import Spotify from "@auth/core/providers/spotify"
import Trakt from "@auth/core/providers/trakt"
import Twitch from "@auth/core/providers/twitch"
import Twitter from "@auth/core/providers/twitter"
import Vk from "@auth/core/providers/vk"
import Wikimedia from "@auth/core/providers/wikimedia"
import WorkOS from "@auth/core/providers/workos"
// // Prisma
// import { PrismaClient } from "@prisma/client"
@@ -66,7 +66,7 @@ import WorkOS from "next-auth/providers/workos"
// secret: process.env.SUPABASE_SERVICE_ROLE_KEY,
// })
export const authOptions: NextAuthOptions = {
export const authOptions: AuthOptions = {
// adapter,
// debug: process.env.NODE_ENV !== "production",
theme: {
@@ -129,4 +129,26 @@ if (authOptions.adapter) {
// )
}
export default NextAuth(authOptions)
// TODO: move to next-auth/edge
function Auth(...args: any[]) {
const envSecret = process.env.AUTH_SECRET ?? process.env.NEXTAUTH_SECRET
const envTrustHost = !!(process.env.NEXTAUTH_URL ?? process.env.AUTH_TRUST_HOST ?? process.env.VERCEL ?? process.env.NODE_ENV !== "production")
if (args.length === 1) {
return async (req: Request) => {
args[0].secret ??= envSecret
args[0].trustHost ??= envTrustHost
return await AuthHandler(req, args[0])
}
}
args[1].secret ??= envSecret
args[1].trustHost ??= envTrustHost
return AuthHandler(args[0], args[1])
}
// export default Auth(authOptions)
export default function handle(request: Request) {
return Auth(request, authOptions)
}
export const config = { runtime: "experimental-edge" }

View File

@@ -1,9 +1,9 @@
// This is an example of to protect an API route
import { getServerSession } from "next-auth/next"
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "../auth/[...nextauth]"
export default async (req, res) => {
const session = await getServerSession(req, res, authOptions)
const session = await unstable_getServerSession(req, res, authOptions)
if (session) {
res.send({

View File

@@ -1,8 +1,8 @@
// This is an example of how to access a session from an API route
import { getServerSession } from "next-auth/next"
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "../auth/[...nextauth]"
export default async (req, res) => {
const session = await getServerSession(req, res, authOptions)
const session = await unstable_getServerSession(req, res, authOptions)
res.json(session)
}

View File

@@ -1,11 +1,11 @@
// This is an example of how to query data from Supabase with RLS.
// Learn more about Row Levele Security (RLS): https://supabase.com/docs/guides/auth/row-level-security
import { getServerSession } from "next-auth/next"
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "../auth/[...nextauth]"
import { createClient } from "@supabase/supabase-js"
export default async (req, res) => {
const session = await getServerSession(req, res, authOptions)
const session = await unstable_getServerSession(req, res, authOptions)
if (!session)
return res.send(JSON.stringify({ error: "No session!" }, null, 2))

View File

@@ -1,5 +1,5 @@
// This is an example of how to protect content using server rendering
import { getServerSession } from "next-auth/next"
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "./api/auth/[...nextauth]"
import Layout from "../components/layout"
import AccessDenied from "../components/access-denied"
@@ -26,7 +26,11 @@ export default function Page({ content, session }) {
}
export async function getServerSideProps(context) {
const session = await getServerSession(context.req, context.res, authOptions)
const session = await unstable_getServerSession(
context.req,
context.res,
authOptions
)
let content = null
if (session) {

View File

@@ -1,6 +1,6 @@
import { getServerSession } from "next-auth/next"
import { unstable_getServerSession } from "next-auth/next"
import Layout from "../components/layout"
import { authOptions } from "./api/auth/[...nextauth]"
import { authOptions } from './api/auth/[...nextauth]';
export default function Page() {
// As this page uses Server Side Rendering, the `session` will be already
@@ -12,11 +12,11 @@ export default function Page() {
<Layout>
<h1>Server Side Rendering</h1>
<p>
This page uses the <strong>getServerSession()</strong> method in{" "}
<strong>getServerSideProps()</strong>.
This page uses the <strong>unstable_getServerSession()</strong> method
in <strong>getServerSideProps()</strong>.
</p>
<p>
Using <strong>getServerSession()</strong> in{" "}
Using <strong>unstable_getServerSession()</strong> in{" "}
<strong>getServerSideProps()</strong> is currently the recommended
approach, although the API may still change, if you need to support
Server Side Rendering with authentication.
@@ -40,7 +40,11 @@ export default function Page() {
export async function getServerSideProps(context) {
return {
props: {
session: await getServerSession(context.req, context.res, authOptions),
session: await unstable_getServerSession(
context.req,
context.res,
authOptions
),
},
}
}

View File

@@ -9,7 +9,6 @@ export default function Page() {
useEffect(() => {
if (session) {
console.log(session)
// User is logged in, let's fetch their data.
const { supabaseAccessToken } = session
const supabase = createClient(

View File

@@ -1,6 +1,6 @@
// This is an example of how to protect content using server rendering
// and fetching data from Supabase with RLS enabled.
import { getServerSession } from "next-auth/next"
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "./api/auth/[...nextauth]"
import { createClient } from "@supabase/supabase-js"
import Layout from "../components/layout"
@@ -27,7 +27,11 @@ export default function Page({ data, session }) {
}
export async function getServerSideProps(context) {
const session = await getServerSession(context.req, context.res, authOptions)
const session = await unstable_getServerSession(
context.req,
context.res,
authOptions
)
if (!session)
return {

View File

@@ -23,8 +23,7 @@
{
"name": "next"
}
],
"strictNullChecks": true
]
},
"include": [
"next-env.d.ts",

View File

@@ -9,16 +9,16 @@
</p>
<p align="center" style="align: center;">
<a href="https://npm.im/next-auth">
<img alt="npm" src="https://img.shields.io/npm/v/next-auth?color=green&label=next-auth">
<img alt="npm" src="https://img.shields.io/npm/v/next-auth?color=green&label=next-auth&style=flat-square">
</a>
<a href="https://bundlephobia.com/result?p=next-auth-example">
<img src="https://img.shields.io/bundlephobia/minzip/next-auth?label=next-auth" alt="Bundle Size"/>
<img src="https://img.shields.io/bundlephobia/minzip/next-auth?label=size&style=flat-square" alt="Bundle Size"/>
</a>
<a href="https://www.npmtrends.com/next-auth">
<img src="https://img.shields.io/npm/dm/next-auth?label=next-auth%20downloads" alt="Downloads" />
<img src="https://img.shields.io/npm/dm/next-auth?label=downloads&style=flat-square" alt="Downloads" />
</a>
<a href="https://npm.im/next-auth">
<img src="https://img.shields.io/badge/npm-TypeScript-blue" alt="TypeScript" />
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
</a>
</p>
</p>

View File

@@ -26,7 +26,7 @@
},
"devDependencies": {
"@types/node": "^17",
"@types/react": "^18.0.37",
"@types/react": "^18.0.15",
"typescript": "^4"
}
}

View File

@@ -53,7 +53,6 @@ export const authOptions: NextAuthOptions = {
],
theme: {
colorScheme: "light",
logo: "https://next-auth.js.org/img/logo/logo-sm.png",
},
callbacks: {
async jwt({ token }) {

View File

@@ -1,5 +1,5 @@
// This is an example of to protect an API route
import { getServerSession } from "next-auth/next"
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "../auth/[...nextauth]"
import type { NextApiRequest, NextApiResponse } from "next"
@@ -8,7 +8,7 @@ export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await getServerSession(req, res, authOptions)
const session = await unstable_getServerSession(req, res, authOptions)
if (session) {
return res.send({

View File

@@ -1,5 +1,5 @@
// This is an example of how to access a session from an API route
import { getServerSession } from "next-auth"
import { unstable_getServerSession } from "next-auth"
import { authOptions } from "../auth/[...nextauth]"
import type { NextApiRequest, NextApiResponse } from "next"
@@ -8,6 +8,6 @@ export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await getServerSession(req, res, authOptions)
const session = await unstable_getServerSession(req, res, authOptions)
res.send(JSON.stringify(session, null, 2))
}

View File

@@ -1,4 +1,4 @@
import { getServerSession } from "next-auth/next"
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "./api/auth/[...nextauth]"
import Layout from "../components/layout"
@@ -12,11 +12,11 @@ export default function ServerSidePage({ session }: { session: Session }) {
<Layout>
<h1>Server Side Rendering</h1>
<p>
This page uses the <strong>getServerSession()</strong> method in{" "}
<strong>getServerSideProps()</strong>.
This page uses the <strong>unstable_getServerSession()</strong> method
in <strong>getServerSideProps()</strong>.
</p>
<p>
Using <strong>getServerSession()</strong> in{" "}
Using <strong>unstable_getServerSession()</strong> in{" "}
<strong>getServerSideProps()</strong> is the recommended approach if you
need to support Server Side Rendering with authentication.
</p>
@@ -37,7 +37,11 @@ export default function ServerSidePage({ session }: { session: Session }) {
export async function getServerSideProps(context: GetServerSidePropsContext) {
return {
props: {
session: await getServerSession(context.req, context.res, authOptions),
session: await unstable_getServerSession(
context.req,
context.res,
authOptions
),
},
}
}

View File

@@ -0,0 +1,5 @@
GITHUB_ID=
GITHUB_SECRET=
# On UNIX systems you can use `openssl rand -hex 32` or
# https://generate-secret.vercel.app/32 to generate a secret.
AUTH_SECRET=

View File

@@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

View File

@@ -0,0 +1,20 @@
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
plugins: ['svelte3', '@typescript-eslint'],
ignorePatterns: ['*.cjs'],
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
settings: {
'svelte3/typescript': () => require('typescript')
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020
},
env: {
browser: true,
es2017: true,
node: true
}
};

12
apps/example-sveltekit/.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
.vercel
.output
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

View File

@@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

View File

@@ -0,0 +1,6 @@
{
"semi": false,
"plugins": ["prettier-plugin-svelte"],
"pluginSearchDirs": ["."],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

View File

@@ -0,0 +1,28 @@
> 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).
<p align="center">
<br/>
<a href="https://next-auth.js.org" target="_blank"><img width="150px" src="https://next-auth.js.org/img/logo/logo-sm.png" /></a>
<h3 align="center">Auth.js Example App with <a href="https://kit.svelte.dev">SvelteKit</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/sveltekit">
<img alt="npm" src="https://img.shields.io/npm/v/@auth/sveltekit?color=green&label=@auth/sveltekit&style=flat-square">
</a>
<a href="https://bundlephobia.com/result?p=sveltekit-auth-example">
<img src="https://img.shields.io/bundlephobia/minzip/@auth/sveltekit?label=size&style=flat-square" alt="Bundle Size"/>
</a>
<a href="https://www.npmtrends.com/@auth/sveltekit">
<img src="https://img.shields.io/npm/dm/@auth/sveltekit?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
- [sveltekit.authjs.dev](https://sveltekit.authjs.dev)

View File

@@ -0,0 +1,22 @@
{
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"devDependencies": {
"@sveltejs/adapter-auto": "next",
"@sveltejs/kit": "next",
"svelte": "3.55.0",
"svelte-check": "2.10.2",
"typescript": "4.9.4",
"vite": "4.0.1"
},
"dependencies": {
"@auth/core": "latest",
"@auth/sveltekit": "latest"
},
"type": "module"
}

1
apps/example-sveltekit/src/app.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="@auth/sveltekit" />

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.ico" />
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
</head>
<body>
<div>%sveltekit.body%</div>
</body>
</html>

View File

@@ -0,0 +1,7 @@
import SvelteKitAuth from "@auth/sveltekit"
import GitHub from "@auth/core/providers/github"
import { GITHUB_ID, GITHUB_SECRET } from "$env/static/private"
export const handle = SvelteKitAuth({
providers: [GitHub({ clientId: GITHUB_ID, clientSecret: GITHUB_SECRET })],
})

View File

@@ -0,0 +1,12 @@
<script lang="ts">
export let provider: any
</script>
<form action={provider.signinUrl} method="POST">
{#if provider.callbackUrl}
<input type="hidden" name="callbackUrl" value={provider.callbackUrl} />
{/if}
<button type="submit" class="button">
<slot>Sign in with {provider.name}</slot>
</button>
</form>

View File

@@ -0,0 +1,7 @@
import type { LayoutServerLoad } from "./$types"
export const load: LayoutServerLoad = async (event) => {
return {
session: await event.locals.getSession(),
}
}

View File

@@ -0,0 +1,151 @@
<script lang="ts">
import { page } from "$app/stores"
</script>
<div>
<header>
<div class="signedInStatus">
<p class="nojs-show loaded">
{#if $page.data.session}
{#if $page.data.session.user?.image}
<span
style="background-image: url('{$page.data.session.user.image}')"
class="avatar"
/>
{/if}
<span class="signedInText">
<small>Signed in as</small><br />
<strong
>{$page.data.session.user?.email ??
$page.data.session.user?.name}</strong
>
</span>
<a href="/auth/signout" class="button">Sign out</a>
{:else}
<span class="notSignedInText">You are not signed in</span>
<a href="/auth/signin" class="buttonPrimary">Sign in</a>
{/if}
</p>
</div>
<nav>
<ul class="navItems">
<li class="navItem"><a href="/">Home</a></li>
<li class="navItem"><a href="/protected">Protected</a></li>
</ul>
</nav>
</header>
<slot />
</div>
<style>
:global(body) {
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji";
padding: 0 1rem 1rem 1rem;
max-width: 680px;
margin: 0 auto;
background: #fff;
color: #333;
}
:global(li),
:global(p) {
line-height: 1.5rem;
}
:global(a) {
font-weight: 500;
}
:global(hr) {
border: 1px solid #ddd;
}
:global(iframe) {
background: #ccc;
border: 1px solid #ccc;
height: 10rem;
width: 100%;
border-radius: 0.5rem;
filter: invert(1);
}
.nojs-show {
opacity: 1;
top: 0;
}
.signedInStatus {
display: block;
min-height: 4rem;
width: 100%;
}
.loaded {
position: relative;
top: 0;
opacity: 1;
overflow: hidden;
border-radius: 0 0 0.6rem 0.6rem;
padding: 0.6rem 1rem;
margin: 0;
background-color: rgba(0, 0, 0, 0.05);
transition: all 0.2s ease-in;
}
.signedInText,
.notSignedInText {
position: absolute;
padding-top: 0.8rem;
left: 1rem;
right: 6.5rem;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: inherit;
z-index: 1;
line-height: 1.3rem;
}
.signedInText {
padding-top: 0rem;
left: 4.6rem;
}
.avatar {
border-radius: 2rem;
float: left;
height: 2.8rem;
width: 2.8rem;
background-color: white;
background-size: cover;
background-repeat: no-repeat;
}
.button,
.buttonPrimary {
float: right;
margin-right: -0.4rem;
font-weight: 500;
border-radius: 0.3rem;
cursor: pointer;
font-size: 1rem;
line-height: 1.4rem;
padding: 0.7rem 0.8rem;
position: relative;
z-index: 10;
background-color: transparent;
color: #555;
}
.buttonPrimary {
background-color: #346df1;
border-color: #346df1;
color: #fff;
text-decoration: none;
padding: 0.7rem 1.4rem;
}
.buttonPrimary:hover {
box-shadow: inset 0 0 5rem rgba(0, 0, 0, 0.2);
}
.navItems {
margin-bottom: 2rem;
padding: 0;
list-style: none;
}
.navItem {
display: inline-block;
margin-right: 1rem;
}
</style>

View File

@@ -0,0 +1,7 @@
<h1>SvelteKit Auth Example</h1>
<p>
This is an example site to demonstrate how to use <a
href="https://kit.svelte.dev/">SvelteKit</a
>
with <a href="https://sveltekit.authjs.dev">SvelteKit Auth</a> for authentication.
</p>

View File

@@ -0,0 +1,10 @@
<script lang="ts">
import { page } from "$app/stores"
</script>
<h1>Protected page</h1>
<p>
This is a protected content. You can access this content because you are
signed in.
</p>
<p>Session expiry: {$page.data.session?.expires}</p>

View File

@@ -0,0 +1,10 @@
import { redirect } from "@sveltejs/kit"
import type { PageLoad } from "./$types"
export const load: PageLoad = async ({ parent }) => {
const { session } = await parent()
if (!session?.user) {
throw redirect(302, "/")
}
return {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,15 @@
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/kit/vite';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
adapter: adapter()
}
};
export default config;

View File

@@ -0,0 +1,17 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

View File

@@ -0,0 +1,8 @@
import { sveltekit } from "@sveltejs/kit/vite"
/** @type {import('vite').UserConfig} */
const config = {
plugins: [sveltekit()],
}
export default config

View File

@@ -9,13 +9,13 @@
</p>
<p align="center" style="align: center;">
<a href="https://npm.im/next-auth">
<img alt="npm" src="https://img.shields.io/npm/v/next-auth?color=green&label=next-auth">
<img alt="npm" src="https://img.shields.io/npm/v/next-auth?color=green&label=next-auth&style=flat-square">
</a>
<a href="https://bundlephobia.com/result?p=next-auth-example">
<img src="https://img.shields.io/bundlephobia/minzip/next-auth?label=next-auth" alt="Bundle Size"/>
<img src="https://img.shields.io/bundlephobia/minzip/next-auth?label=bundle&style=flat-square" alt="Bundle Size"/>
</a>
<a href="https://www.npmtrends.com/next-auth">
<img src="https://img.shields.io/npm/dm/next-auth?label=next-auth%20downloads" alt="Downloads" />
<img src="https://img.shields.io/npm/dm/next-auth?label=20downloads&style=flat-square" alt="Downloads" />
</a>
</p>
</p>

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,12 +0,0 @@
root = true
[*]
indent_size = 2
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

View File

@@ -1,4 +0,0 @@
dist
node_modules
tsconfig.json
package.json

View File

@@ -1,10 +0,0 @@
{
"extends": [
"@nuxtjs/eslint-config-typescript"
],
"rules": {
"@typescript-eslint/no-unused-vars": [
"off"
]
}
}

View File

@@ -0,0 +1,7 @@
module.exports = {
root: true,
extends: ['@nuxt/eslint-config'],
rules: {
'vue/multi-word-component-names': 'off'
}
}

View File

@@ -1,52 +1,4 @@
# Dependencies
node_modules
# Logs
*.log*
# Temp directories
.temp
.tmp
.cache
# Yarn
**/.yarn/cache
**/.yarn/*state*
# Generated dirs
dist
# Nuxt
.nuxt
.output
.vercel_build_output
.build-*
.env
.netlify
# Env
.env
# Testing
reports
coverage
*.lcov
.nyc_output
# VSCode
.vscode
# Intellij idea
*.iml
.idea
# OSX
.DS_Store
.AppleDouble
.LSOverride
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
.vercel
dist
output

View File

@@ -1 +0,0 @@
imports.autoImport=false

View File

@@ -1,21 +1,13 @@
# NextAuth + Nuxt 3 Playground
> The example repository is maintained from a [monorepo](https://github.com/nextauthjs/next-auth/tree/main/apps/playground-nuxt). Pull Requests should be opened against [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth).
NextAuth.js is committed to bringing easy authentication to other frameworks. [#2294](https://github.com/nextauthjs/next-auth/issues/2294)
Nuxt 3 support with NextAuth.js is currently experimental. This directory contains a minimal, proof-of-concept application. Parts of this is expected to be abstracted away into a package like` @next-auth/nuxt.`
This package uses Nuxt's [module starter](https://github.com/nuxt/starter/tree/module).
Demo: https://next-auth-nuxt-demo.vercel.app
Nuxt 3 support with Auth.js is currently experimental. This directory contains a minimal, proof-of-concept application. Parts of this is expected to be abstracted away into a package like `@auth/nuxt`.
## Getting Started
### Add the module to the modules section of `nuxt.config.ts`:
1. Setup your environment variables in `nuxt.config.ts`:
```ts
export default defineNuxtConfig({
// temporary module name.
modules: ['next-auth-nuxt'],
// https://v3.nuxtjs.org/migration/runtime-config#runtime-config
runtimeConfig: {
secret: process.env.NEXTAUTH_SECRET
@@ -23,86 +15,36 @@ export default defineNuxtConfig({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET
}
},
// https://v3.nuxtjs.org/guide/concepts/esm#aliasing-libraries
// Fix for GithubProvider (or whichever provider you choose) is not a function error in Vite
alias: {
'next-auth/providers/github': 'node_modules/next-auth/providers/github.js'
}
})
```
### Add API route
2. Set up Auth.js options
To add `NextAuth.js` to a project create a file called `[...].ts` in `server/api/auth`. This contains the dynamic route handler for NextAuth.js which will also contain all of your global NextAuth.js configurations.
Go to the API handler file (`server/api/auth/[...].ts`) and setup your providers. This file contains the dynamic route handler for Auth.js which will also contain all of your global Auth.js configurations.
Here's an example of what it looks like:
```ts
// ~/server/api/auth/[...].ts
import { NextAuthNuxtHandler } from 'next-auth-nuxt/handler'
import GithubProvider from 'next-auth/providers/github'
// server/api/auth/[...].ts
import { NuxtAuthHandler } from '@/lib/auth/server'
import GithubProvider from '@auth/core/providers/github'
import type { AuthOptions } from '@auth/core'
const runtimeConfig = useRuntimeConfig()
export const authOptions = {
export const authOptions: AuthOptions = {
secret: runtimeConfig.secret,
providers: [
GithubProvider({
clientId: runtimeConfig.github.clientId,
clientSecret: runtimeConfig.github.clientSecret
}),
],
})
]
}
export default NextAuthNuxtHandler(authOptions)
export default NuxtAuthHandler(authOptions)
```
All requests to `/api/auth/*` (`signIn`, `callback`, `signOut`, etc.) will automatically be handled by NextAuth.js.
### Frontend - Add Vue Composable
The `useSession()` Vue Composable is the easiest way to check if someone is signed in.
```html
<script setup lang="ts">
const { data: session } = useSession()
</script>
<template>
<div v-if="session">
Signed in as {{ session.user.email }} <br />
<button @click="signOut">Sign out</button>
</div>
<div v-else>
Not signed in <br />
<button @click="signIn">Sign in</button>
</div>
</template>
```
### Backend - API Route
To protect an API Route, you can use the `getServerSession()` method.
```ts
import { getServerSession } from 'next-auth-nuxt/handler'
import { authOptions } from '~/server/api/auth/[...]'
export default defineEventHandler(async (event) => {
const session = await getServerSession(event, authOptions)
if (session) {
return {
content: 'This is protected content. You can access this content because you are signed in.'
}
}
return {
error: 'You must be signed in to view the protected content on this page.'
}
})
```
## Development
- Run `pnpm dev:generate` to generate type stubs.
- Use `pnpm dev` to start `playground` in development mode.
All requests to `/api/auth/*` (`signIn`, `callback`, `signOut`, etc.) will automatically be handled by Auth.js.

View File

@@ -0,0 +1,30 @@
<template>
<div>
<Header />
<NuxtPage />
</div>
</template>
<style>
body {
font-family: -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
padding: 0 1rem 1rem 1rem;
max-width: 680px;
margin: 0 auto;
background: #fff;
color: #333;
}
li,
p {
line-height: 1.5rem;
}
a {
font-weight: 500;
}
hr {
border: 1px solid #ddd;
}
</style>

View File

@@ -1 +0,0 @@
export * from './dist/runtime/client'

View File

@@ -0,0 +1,139 @@
<script setup lang="ts">
import { signIn, signOut } from '@/lib/auth/client'
const session = useSession()
</script>
<template>
<header>
<div class="signedInStatus">
<p :class="['nojs-show', 'loaded']">
<template v-if="session">
<span v-if="session.user?.image" :style="{ backgroundImage: `url(${session.user.image})` }" class="avatar" />
<span class="signedInText">
<small>Signed in as</small><br>
<strong>{{ session.user?.email || session.user?.name }}</strong>
</span>
<a href="/api/auth/signout" class="button" @click.prevent="signOut">Sign out</a>
</template>
<template v-else>
<span class="notSignedInText">You are not signed in</span>
<a href="/api/auth/signin" class="buttonPrimary" @click.prevent="signIn">Sign in</a>
</template>
</p>
</div>
<nav>
<ul class="navItems">
<li class="navItem">
<NuxtLink to="/">
Home
</NuxtLink>
</li>
<li class="navItem">
<NuxtLink to="/protected">
Protected
</NuxtLink>
</li>
</ul>
</nav>
</header>
</template>
<style>
.nojs-show {
opacity: 1;
top: 0;
}
.signedInStatus {
display: block;
min-height: 4rem;
width: 100%;
}
.loading,
.loaded {
position: relative;
top: 0;
opacity: 1;
overflow: hidden;
border-radius: 0 0 .6rem .6rem;
padding: .6rem 1rem;
margin: 0;
background-color: rgba(0,0,0,.05);
transition: all 0.2s ease-in;
}
.loading {
top: -2rem;
opacity: 0;
}
.signedInText,
.notSignedInText {
position: absolute;
padding-top: .8rem;
left: 1rem;
right: 6.5rem;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: inherit;
z-index: 1;
line-height: 1.3rem;
}
.signedInText {
padding-top: 0rem;
left: 4.6rem;
}
.avatar {
border-radius: 2rem;
float: left;
height: 2.8rem;
width: 2.8rem;
background-color: white;
background-size: cover;
background-repeat: no-repeat;
}
.button,
.buttonPrimary {
float: right;
margin-right: -.4rem;
font-weight: 500;
border-radius: .3rem;
cursor: pointer;
font-size: 1rem;
line-height: 1.4rem;
padding: .7rem .8rem;
position: relative;
z-index: 10;
background-color: transparent;
color: #555;
}
.buttonPrimary {
background-color: #346df1;
border-color: #346df1;
color: #fff;
text-decoration: none;
padding: .7rem 1.4rem;
}
.buttonPrimary:hover {
box-shadow: inset 0 0 5rem rgba(0,0,0,0.2)
}
.navItems {
margin-bottom: 2rem;
padding: 0;
list-style: none;
}
.navItem {
display: inline-block;
margin-right: 1rem;
}
</style>

View File

@@ -0,0 +1,5 @@
import { Session } from '@auth/core'
export default function useSession() {
return useState<Session | null>('session', () => null)
}

View File

@@ -1 +0,0 @@
export * from './dist/runtime/server/handler'

View File

@@ -0,0 +1,107 @@
import type {
LiteralUnion,
SignInOptions,
SignInAuthorizationParams,
SignOutParams,
} from './types'
import type {
BuiltInProviderType,
RedirectableProviderType,
} from '@auth/core/providers'
/**
* Client-side method to initiate a signin flow
* or send the user to the signin page listing all possible providers.
* Automatically adds the CSRF token to the request.
*
* [Documentation](https://next-auth.js.org/getting-started/client#signin)
*/
export async function signIn<
P extends RedirectableProviderType | undefined = undefined
>(
providerId?: LiteralUnion<
P extends RedirectableProviderType
? P | BuiltInProviderType
: BuiltInProviderType
>,
options?: SignInOptions,
authorizationParams?: SignInAuthorizationParams
) {
const { callbackUrl = window.location.href, redirect = true } = options ?? {}
// TODO: Support custom providers
const isCredentials = providerId === "credentials"
const isEmail = providerId === "email"
const isSupportingReturn = isCredentials || isEmail
// TODO: Handle custom base path
const signInUrl = `/api/auth/${
isCredentials ? "callback" : "signin"
}/${providerId}`
const _signInUrl = `${signInUrl}?${new URLSearchParams(authorizationParams)}`
// TODO: Handle custom base path
// TODO: Remove this since Sveltekit offers the CSRF protection via origin check
const { csrfToken } = await $fetch("/api/auth/csrf")
console.log(_signInUrl)
const res = await fetch(_signInUrl, {
method: "post",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"X-Auth-Return-Redirect": "1",
},
// @ts-expect-error -- ignore
body: new URLSearchParams({
...options,
csrfToken,
callbackUrl,
}),
})
const data = await res.clone().json()
const error = new URL(data.url).searchParams.get("error")
if (redirect || !isSupportingReturn || !error) {
// TODO: Do not redirect for Credentials and Email providers by default in next major
window.location.href = data.url ?? callbackUrl
// If url contains a hash, the browser does not reload the page. We reload manually
if (data.url.includes("#")) window.location.reload()
return
}
return res
}
/**
* Signs the user out, by removing the session cookie.
* Automatically adds the CSRF token to the request.
*
* [Documentation](https://next-auth.js.org/getting-started/client#signout)
*/
export async function signOut(options?: SignOutParams) {
const { callbackUrl = window.location.href } = options ?? {}
// TODO: Custom base path
// TODO: Remove this since Sveltekit offers the CSRF protection via origin check
const csrfTokenResponse = await fetch("/api/auth/csrf")
const { csrfToken } = await csrfTokenResponse.json()
const res = await fetch(`/api/auth/signout`, {
method: "post",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"X-Auth-Return-Redirect": "1",
},
body: new URLSearchParams({
csrfToken,
callbackUrl,
}),
})
const data = await res.json()
const url = data.url ?? callbackUrl
window.location.href = url
// If url contains a hash, the browser does not reload the page. We reload manually
if (url.includes("#")) window.location.reload()
}

View File

@@ -0,0 +1,45 @@
import { AuthHandler, AuthOptions, Session } from '@auth/core'
import { fromNodeMiddleware, H3Event } from 'h3'
import getURL from 'requrl'
import { createMiddleware } from "@hattip/adapter-node";
export function NuxtAuthHandler (options: AuthOptions) {
async function handler(ctx: { request: Request }) {
options.trustHost ??= true
return AuthHandler(ctx.request, options)
}
const middleware = createMiddleware(handler)
return fromNodeMiddleware(middleware)
}
export async function getSession(
event: H3Event,
options: AuthOptions
): Promise<Session | null> {
options.trustHost ??= true
const headers = getRequestHeaders(event)
const nodeHeaders = new Headers()
const url = new URL('/api/auth/session', getURL(event.node.req))
Object.keys(headers).forEach((key) => {
nodeHeaders.append(key, headers[key] as any)
})
const response = await AuthHandler(
new Request(url, { headers: nodeHeaders }),
options
)
const { status = 200 } = response
const data = await response.json()
if (!data || !Object.keys(data).length) return null
if (status === 200) return data
throw new Error(data.message)
}

View File

@@ -0,0 +1,42 @@
// Taken from next-auth/react
import type { BuiltInProviderType, ProviderType } from '@auth/core/providers'
/**
* Util type that matches some strings literally, but allows any other string as well.
* @source https://github.com/microsoft/TypeScript/issues/29729#issuecomment-832522611
*/
export declare type LiteralUnion<T extends U, U = string> = T | (U & Record<never, never>);
export interface ClientSafeProvider {
id: LiteralUnion<BuiltInProviderType>;
name: string;
type: ProviderType;
signinUrl: string;
callbackUrl: string;
}
export interface SignInOptions extends Record<string, unknown> {
/**
* Specify to which URL the user will be redirected after signing in. Defaults to the page URL the sign-in is initiated from.
*
* [Documentation](https://next-auth.js.org/getting-started/client#specifying-a-callbackurl)
*/
callbackUrl?: string;
/** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option) */
redirect?: boolean;
}
export interface SignInResponse {
error: string | undefined;
status: number;
ok: boolean;
url: string | null;
}
/** Match `inputType` of `new URLSearchParams(inputType)` */
export declare type SignInAuthorizationParams = string | string[][] | Record<string, string> | URLSearchParams;
/** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1) */
export interface SignOutResponse {
url: string;
}
export interface SignOutParams<R extends boolean = true> {
/** [Documentation](https://next-auth.js.org/getting-started/client#specifying-a-callbackurl-1) */
callbackUrl?: string;
/** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1 */
redirect?: R;
}

View File

@@ -1,9 +1,4 @@
import MyModule from '../src/module'
export default defineNuxtConfig({
modules: [
MyModule
],
// https://v3.nuxtjs.org/migration/runtime-config#runtime-config
runtimeConfig: {
secret: process.env.NEXTAUTH_SECRET,
@@ -12,9 +7,11 @@ export default defineNuxtConfig({
clientSecret: process.env.GITHUB_CLIENT_SECRET
}
},
// https://v3.nuxtjs.org/guide/concepts/esm#aliasing-libraries
// Fix for GithubProvider is not a function error in Vite
alias: {
'next-auth/providers/github': 'node_modules/next-auth/providers/github.js'
vite: {
define: {
'process.env.NEXTAUTH_URL': JSON.stringify(process.env.NEXTAUTH_URL),
'process.env.AUTH_TRUST_HOST': JSON.stringify(process.env.AUTH_TRUST_HOST),
'process.env.VERCEL_URL': JSON.stringify(process.env.VERCEL_URL),
}
}
})

View File

@@ -1,49 +1,22 @@
{
"name": "next-auth-nuxt",
"type": "module",
"version": "0.0.0",
"packageManager": "pnpm@7.1.1",
"license": "MIT",
"main": "./dist/module.cjs",
"types": "./dist/types.d.ts",
"exports": {
".": {
"import": "./dist/module.mjs",
"require": "./dist/module.cjs"
},
"./handler": {
"import": "./dist/runtime/server/handler.mjs",
"types": "./dist/runtime/server/handler.d.ts"
},
"./client": {
"import": "./dist/runtime/client/index.mjs",
"types": "./dist/runtime/client/index.d.ts"
}
},
"files": [
"dist",
"handler.d.ts",
"client.d.ts"
],
"name": "playground-nuxt",
"private": true,
"scripts": {
"prepack": "nuxt-module-build",
"dev": "pnpm prepack && nuxi dev playground",
"dev:build": "nuxi build playground",
"dev:build:vercel": "NITRO_PRESET=vercel nuxi build playground",
"dev:prepare": "nuxt-module-build --stub && nuxi prepare playground"
},
"dependencies": {
"@nuxt/kit": "^3.0.0-rc.13",
"h3": "^0.8.6",
"next-auth": "^4.16.2",
"pathe": "^0.3.9"
"build": "nuxt build",
"dev": "export NODE_OPTIONS='--no-experimental-fetch' && nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"devDependencies": {
"@nuxt/module-builder": "^0.2.0",
"@nuxt/schema": "^3.0.0-rc.12",
"@nuxtjs/eslint-config-typescript": "^11.0.0",
"eslint": "^8.26.0",
"nuxt": "^3.0.0-rc.13",
"next-auth-nuxt": "workspace:*"
"@nuxt/eslint-config": "^0.1.1",
"eslint": "^8.29.0",
"h3": "1.0.2",
"nuxt": "3.0.0"
},
"dependencies": {
"@auth/core": "workspace:*",
"@hattip/adapter-node": "^0.0.22",
"requrl": "^3.0.2"
}
}

View File

@@ -0,0 +1,8 @@
<template>
<div>
<h1>Nuxt Auth Example</h1>
<p>
This is an example site to demonstrate how to use <a href="https://v3.nuxtjs.org/">Nuxt 3</a> with <a href="https://authjs.dev/">Auth.js</a> for authentication.
</p>
</div>
</template>

View File

@@ -0,0 +1,18 @@
<script setup lang="ts">
const session = useSession()
definePageMeta({
middleware: 'auth'
})
</script>
<template>
<div>
<h1>Protected Page</h1>
<p>
This is a protected content. You can access this content because you are
signed in.
</p>
<p>Session expiry: {{ session?.expires }}</p>
</div>
</template>

View File

@@ -1,40 +0,0 @@
<template>
<div>
<Header />
<NuxtPage />
<Footer />
</div>
</template>
<style>
body {
font-family: -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
padding: 0 1rem 1rem 1rem;
max-width: 680px;
margin: 0 auto;
background: #fff;
color: #333;
}
li,
p {
line-height: 1.5rem;
}
a {
font-weight: 500;
}
hr {
border: 1px solid #ddd;
}
iframe {
background: #ccc;
border: 1px solid #ccc;
height: 10rem;
width: 100%;
border-radius: .5rem;
filter: invert(1);
}
</style>

View File

@@ -1,8 +0,0 @@
<template>
<div>
<h1>Access Denied</h1>
<p>
<a href="/api/auth/signin">You must be signed in to view this page</a>
</p>
</div>
</template>

View File

@@ -1,30 +0,0 @@
<template>
<footer class="fotter">
<hr>
<ul class="navItems">
<li class="navItem">
<a href="https://github.com/nextauthjs/next-auth/tree/main/apps/playground-nuxt">Demo GitHub</a>
</li>
<li class="navItem">
<a href="https://next-auth.js.org">Next.js Documentation</a>
</li>
</ul>
</footer>
</template>
<style>
.footer {
margin-top: 2rem;
}
.navItems {
margin-bottom: 1rem;
padding: 0;
list-style: none;
}
.navItem {
display: inline-block;
margin-right: 1rem;
}
</style>

View File

@@ -1,155 +0,0 @@
<script setup lang="ts">
import { useSession, signIn, signOut, computed } from '#imports'
const { data: session, status } = useSession()
const loading = computed(() => status.value === 'loading')
</script>
<template>
<header>
<div class="signedInStatus">
<p :class="['nojs-show', !session && loading ? 'loading' : 'loaded']">
<template v-if="session">
<span v-if="session.user?.image" :style="{ backgroundImage: `url(${session.user.image})` }" class="avatar" />
<span class="signedInText">
<small>Signed in as</small><br>
<strong>{{ session.user?.email || session.user?.name }}</strong>
</span>
<a href="/api/auth/signout" class="button" @click.prevent="signOut">Sign out</a>
</template>
<template v-else>
<span class="notSignedInText">You are not signed in</span>
<a href="/api/auth/signin" class="buttonPrimary" @click.prevent="signIn">Sign in</a>
</template>
</p>
</div>
<nav>
<ul class="navItems">
<li class="navItem">
<NuxtLink to="/">
Home
</NuxtLink>
</li>
<li class="navItem">
<NuxtLink to="/client">
Client
</NuxtLink>
</li>
<li class="navItem">
<NuxtLink to="/server">
Server
</NuxtLink>
</li>
<li class="navItem">
<NuxtLink to="/protected">
Protected
</NuxtLink>
</li>
<li class="navItem">
<NuxtLink to="/api-example">
API
</NuxtLink>
</li>
</ul>
</nav>
</header>
</template>
<style>
.nojs-show {
opacity: 1;
top: 0;
}
.signedInStatus {
display: block;
min-height: 4rem;
width: 100%;
}
.loading,
.loaded {
position: relative;
top: 0;
opacity: 1;
overflow: hidden;
border-radius: 0 0 .6rem .6rem;
padding: .6rem 1rem;
margin: 0;
background-color: rgba(0,0,0,.05);
transition: all 0.2s ease-in;
}
.loading {
top: -2rem;
opacity: 0;
}
.signedInText,
.notSignedInText {
position: absolute;
padding-top: .8rem;
left: 1rem;
right: 6.5rem;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: inherit;
z-index: 1;
line-height: 1.3rem;
}
.signedInText {
padding-top: 0rem;
left: 4.6rem;
}
.avatar {
border-radius: 2rem;
float: left;
height: 2.8rem;
width: 2.8rem;
background-color: white;
background-size: cover;
background-repeat: no-repeat;
}
.button,
.buttonPrimary {
float: right;
margin-right: -.4rem;
font-weight: 500;
border-radius: .3rem;
cursor: pointer;
font-size: 1rem;
line-height: 1.4rem;
padding: .7rem .8rem;
position: relative;
z-index: 10;
background-color: transparent;
color: #555;
}
.buttonPrimary {
background-color: #346df1;
border-color: #346df1;
color: #fff;
text-decoration: none;
padding: .7rem 1.4rem;
}
.buttonPrimary:hover {
box-shadow: inset 0 0 5rem rgba(0,0,0,0.2)
}
.navItems {
margin-bottom: 2rem;
padding: 0;
list-style: none;
}
.navItem {
display: inline-block;
margin-right: 1rem;
}
</style>

View File

@@ -1,4 +0,0 @@
{
"name": "playground",
"private": true
}

View File

@@ -1,15 +0,0 @@
<template>
<div>
<h1>API Example</h1>
<p>The examples below show responses from the example API endpoints.</p>
<p>
<em>You must be signed in to see responses.</em>
</p>
<h2>Session</h2>
<p>/api/examples/session</p>
<iframe src="/api/examples/session" />
<h2>JSON Web Token</h2>
<p>/api/examples/jwt</p>
<iframe src="/api/examples/jwt" />
</div>
</template>

View File

@@ -1,18 +0,0 @@
<template>
<div>
<h1>Client Side Rendering</h1>
<p>
This page uses the <strong>useSession()</strong> Vue Composable in the <strong>&lt;Header/&gt;</strong> component.
</p>
<p>
The <strong>useSession()</strong> Vue Composable is easy to use and allows pages to render very quickly.
</p>
<p>
The advantage of this approach is that session state is shared between pages by using a provided session via <strong>Vue Plugin</strong> so
that navigation between pages using <strong>useSession()</strong> is very fast.
</p>
<p>
The disadvantage of <strong>useSession()</strong> is that it requires client side JavaScript.
</p>
</div>
</template>

View File

@@ -1,8 +0,0 @@
<template>
<div>
<h1>Nuxt 3 + NextAuth.js Example</h1>
<p>
This is an example site to demonstrate how to use <a href="https://v3.nuxtjs.org/">Nuxt 3</a> with <a href="https://next-auth.js.org">NextAuth.js</a> for authentication.
</p>
</div>
</template>

View File

@@ -1,19 +0,0 @@
<script setup lang="ts">
import { useSession, useFetch, useLazyFetch } from '#imports'
import AccessDenied from '~/components/AccessDenied.vue'
const { data: session } = useSession()
const { data } = await useLazyFetch('/api/examples/protected', {
server: false
})
</script>
<template>
<div>
<AccessDenied v-if="!session" />
<template v-else>
<h1>Protected Page</h1>
<p><strong>{{ data?.content || "\u00a0" }}</strong></p>
</template>
</div>
</template>

View File

@@ -1,24 +0,0 @@
<script setup lang="ts">
import { useFetch } from '#imports'
await useFetch('/api/examples/session')
</script>
<template>
<div>
<h1>Server Side Rendering</h1>
<p>
This page uses the <strong>getServerSession()</strong> method inside an api route and is fetched using the <strong>useFetch()</strong> composable.
</p>
<p>
Using <strong>getServerSession()</strong> is the recommended approach if you need to
support Server Side Rendering with authentication.
</p>
<p>
The advantage of Server Side Rendering is this page does not require client side JavaScript.
</p>
<p>
The disadvantage of Server Side Rendering is that this page is slower to render.
</p>
</div>
</template>

View File

@@ -1,17 +0,0 @@
import { NextAuthNuxtHandler } from 'next-auth-nuxt/handler'
import GithubProvider from 'next-auth/providers/github'
import type { NextAuthOptions } from 'next-auth'
const runtimeConfig = useRuntimeConfig()
export const authOptions: NextAuthOptions = {
secret: runtimeConfig.secret,
providers: [
GithubProvider({
clientId: runtimeConfig.github.clientId,
clientSecret: runtimeConfig.github.clientSecret
})
]
}
export default NextAuthNuxtHandler(authOptions)

View File

@@ -1,10 +0,0 @@
import { getToken } from 'next-auth/jwt'
export default defineEventHandler(async (event) => {
// @ts-expect-error: cookies property is not present in h3
event.req.cookies = parseCookies(event)
const token = await getToken({
req: event.req
})
return token
})

View File

@@ -1,16 +0,0 @@
import { getServerSession } from 'next-auth-nuxt/handler'
import { authOptions } from '../auth/[...]'
export default defineEventHandler(async (event) => {
const session = await getServerSession(event, authOptions)
if (session) {
return {
content: 'This is protected content. You can access this content because you are signed in.'
}
}
return {
error: 'You must be signed in to view the protected content on this page.'
}
})

Some files were not shown because too many files have changed in this diff Show More