mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
7 Commits
@auth/supa
...
fix/update
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b57dea40e2 | ||
|
|
1387445600 | ||
|
|
b2d7f5529f | ||
|
|
7c101f5a73 | ||
|
|
92c96a267f | ||
|
|
b31bad15fe | ||
|
|
d225bbe600 |
@@ -5,7 +5,7 @@ const path = require("path")
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: "@typescript-eslint/parser",
|
||||
extends: ["standard-with-typescript", "prettier/prettier"],
|
||||
extends: ["standard-with-typescript", "prettier"],
|
||||
rules: {
|
||||
camelcase: "off",
|
||||
"@typescript-eslint/naming-convention": "off",
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -29,8 +29,6 @@ jobs:
|
||||
cache: "pnpm"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
- name: Run tests
|
||||
run: pnpm test
|
||||
env:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -87,6 +87,7 @@ packages/core/*.d.ts.map
|
||||
packages/core/lib
|
||||
packages/core/providers
|
||||
docs/docs/reference/03-core
|
||||
docs/docs/reference/04-sveltekit
|
||||
|
||||
|
||||
# SvelteKit
|
||||
|
||||
@@ -6,10 +6,11 @@ module.exports = {
|
||||
singleQuote: false,
|
||||
overrides: [
|
||||
{
|
||||
files: "apps/dev/pages/api/auth/[...nextauth].ts",
|
||||
options: {
|
||||
printWidth: 150,
|
||||
},
|
||||
files: [
|
||||
"apps/dev/pages/api/auth/[...nextauth].ts",
|
||||
"docs/{sidebars,docusaurus.config}.js",
|
||||
],
|
||||
options: { printWidth: 150 },
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
title: SvelteKit Auth
|
||||
---
|
||||
|
||||
:::warning
|
||||
`@auth/sveltekit` is currently experimental.
|
||||
:::
|
||||
|
||||
## Installation
|
||||
|
||||
```bash npm2yarn2pnpm
|
||||
npm install @auth/core @auth/sveltekit
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Learn more about `@auth/sveltekit` [here](https://vercel.com/blog/announcing-sveltekit-auth)
|
||||
|
||||
```ts title="src/hooks.server.ts"
|
||||
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 })],
|
||||
})
|
||||
```
|
||||
|
||||
Don't forget to set the `AUTH_SECRET` [environment variable](https://kit.svelte.dev/docs/modules#$env-static-private). This should be a random 32 character string. On unix systems you can use `openssl rand -hex 32` or check out `https://generate-secret.vercel.app/32`.
|
||||
|
||||
When deploying your app outside Vercel, set the `AUTH_TRUST_HOST` variable to `true` for other hosting providers like Cloudflare Pages or Netlify.
|
||||
|
||||
## Signing in and signing out
|
||||
|
||||
```ts
|
||||
<script>
|
||||
import { signIn, signOut } from "@auth/sveltekit/client"
|
||||
import { page } from "$app/stores"
|
||||
</script>
|
||||
|
||||
<h1>SvelteKit Auth Example</h1>
|
||||
<p>
|
||||
{#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?.name ?? "User"}</strong>
|
||||
</span>
|
||||
<button on:click={() => signOut()} class="button">Sign out</button>
|
||||
{:else}
|
||||
<span class="notSignedInText">You are not signed in</span>
|
||||
<button on:click={() => signIn("github")}>Sign In with GitHub</button>
|
||||
{/if}
|
||||
</p>
|
||||
```
|
||||
@@ -179,10 +179,7 @@ const docusaurusConfig = {
|
||||
lastVersion: "current",
|
||||
showLastUpdateAuthor: true,
|
||||
showLastUpdateTime: true,
|
||||
remarkPlugins: [
|
||||
require("@sapphire/docusaurus-plugin-npm2yarn2pnpm").npm2yarn2pnpm,
|
||||
require("remark-github"),
|
||||
],
|
||||
remarkPlugins: [require("@sapphire/docusaurus-plugin-npm2yarn2pnpm").npm2yarn2pnpm, require("remark-github")],
|
||||
versions: {
|
||||
current: {
|
||||
label: "experimental",
|
||||
@@ -200,95 +197,42 @@ const docusaurusConfig = {
|
||||
"docusaurus-plugin-typedoc",
|
||||
{
|
||||
...typedocConfig,
|
||||
id: "core",
|
||||
plugin: ["./tyepdoc"],
|
||||
entryPoints: [
|
||||
"index.ts",
|
||||
"adapters.ts",
|
||||
"errors.ts",
|
||||
"jwt.ts",
|
||||
"types.ts",
|
||||
]
|
||||
.map((e) => `${coreSrc}/${e}`)
|
||||
.concat(providers),
|
||||
entryPoints: ["index.ts", "adapters.ts", "errors.ts", "jwt.ts", "types.ts"].map((e) => `${coreSrc}/${e}`).concat(providers),
|
||||
tsconfig: "../packages/core/tsconfig.json",
|
||||
out: "reference/03-core",
|
||||
watch: process.env.TYPEDOC_WATCH,
|
||||
includeExtension: false,
|
||||
},
|
||||
],
|
||||
[
|
||||
"docusaurus-plugin-typedoc",
|
||||
{
|
||||
...typedocConfig,
|
||||
id: "sveltekit",
|
||||
plugin: ["./tyepdoc"],
|
||||
entryPoints: ["index.ts", "client.ts"].map((e) => `../packages/frameworks-sveltekit/src/lib/${e}`),
|
||||
tsconfig: "../packages/frameworks-sveltekit/tsconfig.json",
|
||||
out: "reference/04-sveltekit",
|
||||
watch: process.env.TYPEDOC_WATCH,
|
||||
includeExtension: false,
|
||||
},
|
||||
],
|
||||
],
|
||||
}
|
||||
|
||||
docusaurusConfig.headTags = [
|
||||
{
|
||||
tagName: "meta",
|
||||
attributes: {
|
||||
charSet: "utf-8",
|
||||
},
|
||||
},
|
||||
{
|
||||
tagName: "link",
|
||||
attributes: {
|
||||
rel: "canonical",
|
||||
href: docusaurusConfig.url,
|
||||
},
|
||||
},
|
||||
{
|
||||
tagName: "meta",
|
||||
attributes: {
|
||||
property: "og:title",
|
||||
content: docusaurusConfig.title,
|
||||
},
|
||||
},
|
||||
{
|
||||
tagName: "meta",
|
||||
attributes: {
|
||||
property: "og:description",
|
||||
content: docusaurusConfig.tagline,
|
||||
},
|
||||
},
|
||||
{
|
||||
tagName: "meta",
|
||||
attributes: {
|
||||
property: "og:image",
|
||||
content: `${docusaurusConfig.url}/img/og-image.png`,
|
||||
},
|
||||
},
|
||||
{
|
||||
tagName: "meta",
|
||||
attributes: {
|
||||
property: "og:url",
|
||||
content: docusaurusConfig.url,
|
||||
},
|
||||
},
|
||||
{
|
||||
tagName: "meta",
|
||||
attributes: {
|
||||
name: "twitter:card",
|
||||
content: "summary_large_image",
|
||||
},
|
||||
},
|
||||
{
|
||||
tagName: "meta",
|
||||
attributes: {
|
||||
name: "twitter:title",
|
||||
content: docusaurusConfig.title,
|
||||
},
|
||||
},
|
||||
{
|
||||
tagName: "meta",
|
||||
attributes: {
|
||||
name: "twitter:description",
|
||||
content: docusaurusConfig.tagline,
|
||||
},
|
||||
},
|
||||
{
|
||||
tagName: "meta",
|
||||
attributes: {
|
||||
name: "twitter:image",
|
||||
content: `${docusaurusConfig.url}/img/og-image.png`,
|
||||
},
|
||||
},
|
||||
{ tagName: "meta", attributes: { charSet: "utf-8" } },
|
||||
{ tagName: "link", attributes: { rel: "canonical", href: docusaurusConfig.url } },
|
||||
{ tagName: "meta", attributes: { property: "og:title", content: docusaurusConfig.title } },
|
||||
{ tagName: "meta", attributes: { property: "og:description", content: docusaurusConfig.tagline } },
|
||||
{ tagName: "meta", attributes: { property: "og:image", content: `${docusaurusConfig.url}/img/og-image.png` } },
|
||||
{ tagName: "meta", attributes: { property: "og:url", content: docusaurusConfig.url } },
|
||||
{ tagName: "meta", attributes: { name: "twitter:card", content: "summary_large_image" } },
|
||||
{ tagName: "meta", attributes: { name: "twitter:title", content: docusaurusConfig.title } },
|
||||
{ tagName: "meta", attributes: { name: "twitter:description", content: docusaurusConfig.tagline } },
|
||||
{ tagName: "meta", attributes: { name: "twitter:image", content: `${docusaurusConfig.url}/img/og-image.png` } },
|
||||
]
|
||||
|
||||
module.exports = docusaurusConfig
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
"snippets": "node ./scripts/generate-snippets"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/core": "workspace:*",
|
||||
"@auth/sveltekit": "workspace:*",
|
||||
"@mdx-js/react": "1.6.22",
|
||||
"@sapphire/docusaurus-plugin-npm2yarn2pnpm": "1.1.4",
|
||||
"classnames": "^2.3.2",
|
||||
|
||||
@@ -34,16 +34,22 @@ module.exports = {
|
||||
label: "Reflections",
|
||||
collapsed: true,
|
||||
className: "reflection-category", // See src/index.css
|
||||
items: [
|
||||
{
|
||||
type: "autogenerated",
|
||||
dirName: "reference/03-core/functions",
|
||||
},
|
||||
{
|
||||
type: "autogenerated",
|
||||
dirName: "reference/03-core/interfaces",
|
||||
},
|
||||
],
|
||||
items: [{ type: "autogenerated", dirName: "reference/03-core" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "@auth/sveltekit",
|
||||
link: { type: "doc", id: "reference/sveltekit/modules/main" },
|
||||
items: [
|
||||
{ type: "autogenerated", dirName: "reference/04-sveltekit/modules" },
|
||||
{
|
||||
type: "category",
|
||||
label: "Reflections",
|
||||
collapsed: true,
|
||||
className: "reflection-category", // See src/index.css
|
||||
items: [{ type: "autogenerated", dirName: "reference/04-sveltekit" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -63,30 +69,18 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "@auth/sveltekit",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "reference/sveltekit/index",
|
||||
},
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Database Adapters",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "reference/adapters/overview",
|
||||
},
|
||||
link: { type: "doc", id: "reference/adapters/overview" },
|
||||
items: [
|
||||
{
|
||||
type: "autogenerated",
|
||||
dirName: "reference/06-adapters",
|
||||
// See: https://github.com/facebook/docusaurus/issues/5689
|
||||
// exclude: ["index"],
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
@@ -97,8 +91,8 @@ module.exports = {
|
||||
dirName: "reference/05-oauth-providers",
|
||||
// See: https://github.com/facebook/docusaurus/issues/5689
|
||||
// exclude: ["index"],
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
"reference/utilities/client",
|
||||
"reference/warnings",
|
||||
|
||||
@@ -281,15 +281,18 @@ html[data-theme="dark"] #carbonads .carbon-poweredby {
|
||||
2. the "main" module would show up twice.
|
||||
See sidebars.js
|
||||
*/
|
||||
.reflection-category, .theme-doc-sidebar-item-link-level-2 [href="/reference/core/modules/main"] {
|
||||
.reflection-category,
|
||||
.theme-doc-sidebar-item-link-level-2 [href="/reference/core/modules/main"],
|
||||
.theme-doc-sidebar-item-link-level-2
|
||||
[href="/reference/sveltekit/modules/main"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
HACK: to hide the "Classes" header and duplicate items together with the "typedoc-plugin-markdown" patch.
|
||||
See: https://github.com/TypeStrong/typedoc/issues/2006
|
||||
*/
|
||||
#classes, h3.anchor + p:has(code, strong) {
|
||||
#classes,
|
||||
h3.anchor + p:has(code, strong) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
import * as React from 'react'
|
||||
import Link from '@docusaurus/Link'
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl'
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
|
||||
import CodeBlock from '@theme/CodeBlock'
|
||||
import Layout from '@theme/Layout'
|
||||
import classnames from 'classnames'
|
||||
import { useEffect } from 'react'
|
||||
import ProviderMarquee from '../components/ProviderMarquee'
|
||||
import styles from './index.module.css'
|
||||
import providers from '../../providers.json'
|
||||
import * as React from "react"
|
||||
import Link from "@docusaurus/Link"
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl"
|
||||
import useDocusaurusContext from "@docusaurus/useDocusaurusContext"
|
||||
import CodeBlock from "@theme/CodeBlock"
|
||||
import Layout from "@theme/Layout"
|
||||
import classnames from "classnames"
|
||||
import { useEffect } from "react"
|
||||
import ProviderMarquee from "../components/ProviderMarquee"
|
||||
import styles from "./index.module.css"
|
||||
import providers from "../../providers.json"
|
||||
|
||||
const providersCount = Object.keys(providers).length + 2 // email, credentials
|
||||
const features = [
|
||||
{
|
||||
title: 'Easy',
|
||||
imageUrl: 'img/undraw_social.svg',
|
||||
title: "Easy",
|
||||
imageUrl: "img/undraw_social.svg",
|
||||
description: (
|
||||
<ul>
|
||||
<li>
|
||||
@@ -31,11 +31,11 @@ const features = [
|
||||
Use with <i>any</i> username / password store
|
||||
</li>
|
||||
</ul>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Flexible',
|
||||
imageUrl: 'img/undraw_authentication.svg',
|
||||
title: "Flexible",
|
||||
imageUrl: "img/undraw_authentication.svg",
|
||||
description: (
|
||||
<ul>
|
||||
<li>
|
||||
@@ -55,11 +55,11 @@ const features = [
|
||||
</li>
|
||||
<li>Choose database sessions or JWT</li>
|
||||
</ul>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Secure',
|
||||
imageUrl: 'img/undraw_secure.svg',
|
||||
title: "Secure",
|
||||
imageUrl: "img/undraw_secure.svg",
|
||||
description: (
|
||||
<ul>
|
||||
<li>Signed, prefixed, server-only cookies</li>
|
||||
@@ -68,18 +68,18 @@ const features = [
|
||||
{/* <li>Tab syncing, auto-revalidation, keepalives</li> */}
|
||||
<li>Doesn't rely on client side JavaScript</li>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
const kFormatter = (num) => {
|
||||
return Math.sign(num) * (Math.abs(num) / 1000).toFixed(1) + 'k'
|
||||
return Math.sign(num) * (Math.abs(num) / 1000).toFixed(1) + "k"
|
||||
}
|
||||
|
||||
function Feature ({ imageUrl, title, description }) {
|
||||
function Feature({ imageUrl, title, description }) {
|
||||
const imgUrl = useBaseUrl(imageUrl)
|
||||
return (
|
||||
<div className={classnames('col col--4', styles.feature)}>
|
||||
<div className={classnames("col col--4", styles.feature)}>
|
||||
{imgUrl && (
|
||||
<div className="text--center">
|
||||
<div className="feature-image-wrapper">
|
||||
@@ -93,28 +93,28 @@ function Feature ({ imageUrl, title, description }) {
|
||||
)
|
||||
}
|
||||
|
||||
export default function Home () {
|
||||
export default function Home() {
|
||||
const context = useDocusaurusContext()
|
||||
const { siteConfig = {} } = context
|
||||
|
||||
useEffect(() => {
|
||||
window
|
||||
.fetch('https://api.github.com/repos/nextauthjs/next-auth')
|
||||
.fetch("https://api.github.com/repos/nextauthjs/next-auth")
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const navLinks = document.getElementsByClassName(
|
||||
'navbar__item navbar__link'
|
||||
"navbar__item navbar__link"
|
||||
)
|
||||
const githubStat = document.createElement('span')
|
||||
const githubStat = document.createElement("span")
|
||||
githubStat.innerHTML = kFormatter(data.stargazers_count)
|
||||
githubStat.className = 'github-counter'
|
||||
githubStat.className = "github-counter"
|
||||
navLinks[4].appendChild(githubStat)
|
||||
})
|
||||
}, [])
|
||||
return (
|
||||
<Layout description={siteConfig.tagline}>
|
||||
<div className="home-wrapper">
|
||||
<header className={classnames('hero', styles.heroBanner)}>
|
||||
<header className={classnames("hero", styles.heroBanner)}>
|
||||
<div className="container">
|
||||
<div className="hero-inner">
|
||||
<img
|
||||
@@ -129,7 +129,7 @@ export default function Home () {
|
||||
<div className={styles.buttons}>
|
||||
<a
|
||||
className={classnames(
|
||||
'button button--outline button--secondary button--lg rounded-pill',
|
||||
"button button--outline button--secondary button--lg rounded-pill",
|
||||
styles.button
|
||||
)}
|
||||
href="https://next-auth-example.vercel.app"
|
||||
@@ -138,7 +138,7 @@ export default function Home () {
|
||||
</a>
|
||||
<a
|
||||
className={classnames(
|
||||
'button button--outline button--secondary button--lg rounded-pill',
|
||||
"button button--outline button--secondary button--lg rounded-pill",
|
||||
styles.button
|
||||
)}
|
||||
href="https://sveltekit-auth-example.vercel.app"
|
||||
@@ -147,10 +147,10 @@ export default function Home () {
|
||||
</a>
|
||||
<Link
|
||||
className={classnames(
|
||||
'button button--primary button--lg rounded-pill',
|
||||
"button button--primary button--lg rounded-pill",
|
||||
styles.button
|
||||
)}
|
||||
to={useBaseUrl('/getting-started/introduction')}
|
||||
to={useBaseUrl("/getting-started/introduction")}
|
||||
>
|
||||
Get Started
|
||||
</Link>
|
||||
@@ -170,7 +170,7 @@ export default function Home () {
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<h2 className={styles.featuresTitle}>
|
||||
<span>Open Source.</span> <span>Full Stack.</span>{' '}
|
||||
<span>Open Source.</span> <span>Full Stack.</span>{" "}
|
||||
<span>Own Your Data.</span>
|
||||
</h2>
|
||||
</div>
|
||||
@@ -198,7 +198,7 @@ export default function Home () {
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<h2 className="text--center" style={{ fontSize: '2.5rem' }}>
|
||||
<h2 className="text--center" style={{ fontSize: "2.5rem" }}>
|
||||
Add authentication in minutes!
|
||||
</h2>
|
||||
</div>
|
||||
@@ -227,7 +227,7 @@ export default function Home () {
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<p className="text--center" style={{ marginTop: '2rem' }}>
|
||||
<p className="text--center" style={{ marginTop: "2rem" }}>
|
||||
<Link
|
||||
to="/getting-started/introduction"
|
||||
className="button button--primary button--lg rounded-pill"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Auth.js
|
||||
|
||||
Authentication for the web.
|
||||
Authentication for the web.
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
"clean": "rm -rf *.js *.d.ts lib providers",
|
||||
"css": "node ./scripts/generate-css.js",
|
||||
"lint": "pnpm prettier --check src && eslint src",
|
||||
"format": "pnpm lint --fix",
|
||||
"format": "pnpm prettier --write . && eslint src --fix",
|
||||
"dev": "pnpm css && tsc -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -210,6 +210,7 @@ export interface AuthConfig {
|
||||
* Pages specified will override the corresponding built-in page.
|
||||
* * **Default value**: `{}`
|
||||
* * **Required**: *No*
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
@@ -349,6 +350,7 @@ export interface AuthConfig {
|
||||
* - ⚠ **This is an advanced option.** Advanced options are passed the same way as basic options,
|
||||
* but **may have complex implications** or side effects.
|
||||
* You should **try to avoid using advanced options** unless you are very comfortable using them.
|
||||
*
|
||||
* @default Boolean(process.env.NEXTAUTH_URL ?? process.env.AUTH_TRUST_HOST ?? process.env.VERCEL)
|
||||
*/
|
||||
trustHost?: boolean
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import type {
|
||||
InternalProvider,
|
||||
SignInPageErrorParam,
|
||||
Theme
|
||||
} from '../../types.js'
|
||||
Theme,
|
||||
} from "../../types.js"
|
||||
|
||||
const signinErrors: Record<
|
||||
Lowercase<SignInPageErrorParam | 'default'>,
|
||||
string
|
||||
Lowercase<SignInPageErrorParam | "default">,
|
||||
string
|
||||
> = {
|
||||
default: 'Unable to sign in.',
|
||||
signin: 'Try signing in with a different account.',
|
||||
oauthsignin: 'Try signing in with a different account.',
|
||||
oauthcallback: 'Try signing in with a different account.',
|
||||
oauthcreateaccount: 'Try signing in with a different account.',
|
||||
emailcreateaccount: 'Try signing in with a different account.',
|
||||
callback: 'Try signing in with a different account.',
|
||||
default: "Unable to sign in.",
|
||||
signin: "Try signing in with a different account.",
|
||||
oauthsignin: "Try signing in with a different account.",
|
||||
oauthcallback: "Try signing in with a different account.",
|
||||
oauthcreateaccount: "Try signing in with a different account.",
|
||||
emailcreateaccount: "Try signing in with a different account.",
|
||||
callback: "Try signing in with a different account.",
|
||||
oauthaccountnotlinked:
|
||||
'To confirm your identity, sign in with the same account you used originally.',
|
||||
emailsignin: 'The e-mail could not be sent.',
|
||||
"To confirm your identity, sign in with the same account you used originally.",
|
||||
emailsignin: "The e-mail could not be sent.",
|
||||
credentialssignin:
|
||||
'Sign in failed. Check the details you provided are correct.',
|
||||
sessionrequired: 'Please sign in to access this page.'
|
||||
"Sign in failed. Check the details you provided are correct.",
|
||||
sessionrequired: "Please sign in to access this page.",
|
||||
}
|
||||
|
||||
export default function SigninPage (props: {
|
||||
export default function SigninPage(props: {
|
||||
csrfToken: string
|
||||
providers: InternalProvider[]
|
||||
callbackUrl: string
|
||||
@@ -37,12 +37,12 @@ export default function SigninPage (props: {
|
||||
callbackUrl,
|
||||
theme,
|
||||
email,
|
||||
error: errorType
|
||||
error: errorType,
|
||||
} = props
|
||||
|
||||
if (typeof document !== 'undefined' && theme.brandColor) {
|
||||
if (typeof document !== "undefined" && theme.brandColor) {
|
||||
document.documentElement.style.setProperty(
|
||||
'--brand-color',
|
||||
"--brand-color",
|
||||
theme.brandColor
|
||||
)
|
||||
}
|
||||
@@ -52,13 +52,13 @@ export default function SigninPage (props: {
|
||||
|
||||
// TODO: move logos
|
||||
const logos =
|
||||
'https://raw.githubusercontent.com/nextauthjs/next-auth/main/packages/next-auth/provider-logos'
|
||||
"https://raw.githubusercontent.com/nextauthjs/next-auth/main/packages/next-auth/provider-logos"
|
||||
return (
|
||||
<div className="signin">
|
||||
{theme.brandColor && (
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `:root {--brand-color: ${theme.brandColor}}`
|
||||
__html: `:root {--brand-color: ${theme.brandColor}}`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -71,8 +71,7 @@ export default function SigninPage (props: {
|
||||
)}
|
||||
{providers.map((provider, i) => (
|
||||
<div key={provider.id} className="provider">
|
||||
{provider.type === 'oauth' || provider.type === 'oidc'
|
||||
? (
|
||||
{provider.type === "oauth" || provider.type === "oidc" ? (
|
||||
<form action={provider.signinUrl} method="POST">
|
||||
<input type="hidden" name="csrfToken" value={csrfToken} />
|
||||
{callbackUrl && (
|
||||
@@ -82,17 +81,17 @@ export default function SigninPage (props: {
|
||||
type="submit"
|
||||
className="button"
|
||||
style={{
|
||||
'--provider-bg': provider.style?.bg ?? '',
|
||||
'--provider-dark-bg': provider.style?.bgDark ?? '',
|
||||
'--provider-color': provider.style?.text ?? '',
|
||||
'--provider-dark-color': provider.style?.textDark ?? ''
|
||||
"--provider-bg": provider.style?.bg ?? "",
|
||||
"--provider-dark-bg": provider.style?.bgDark ?? "",
|
||||
"--provider-color": provider.style?.text ?? "",
|
||||
"--provider-dark-color": provider.style?.textDark ?? "",
|
||||
}}
|
||||
>
|
||||
{provider.style?.logo && (
|
||||
<img
|
||||
id="provider-logo"
|
||||
src={`${
|
||||
provider.style.logo.startsWith('/') ? logos : ''
|
||||
provider.style.logo.startsWith("/") ? logos : ""
|
||||
}${provider.style.logo}`}
|
||||
/>
|
||||
)}
|
||||
@@ -100,20 +99,19 @@ export default function SigninPage (props: {
|
||||
<img
|
||||
id="provider-logo-dark"
|
||||
src={`${
|
||||
provider.style.logo.startsWith('/') ? logos : ''
|
||||
provider.style.logo.startsWith("/") ? logos : ""
|
||||
}${provider.style.logoDark}`}
|
||||
/>
|
||||
)}
|
||||
<span>Sign in with {provider.name}</span>
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
: null}
|
||||
{(provider.type === 'email' || provider.type === 'credentials') &&
|
||||
) : null}
|
||||
{(provider.type === "email" || provider.type === "credentials") &&
|
||||
i > 0 &&
|
||||
providers[i - 1].type !== 'email' &&
|
||||
providers[i - 1].type !== 'credentials' && <hr />}
|
||||
{provider.type === 'email' && (
|
||||
providers[i - 1].type !== "email" &&
|
||||
providers[i - 1].type !== "credentials" && <hr />}
|
||||
{provider.type === "email" && (
|
||||
<form action={provider.signinUrl} method="POST">
|
||||
<input type="hidden" name="csrfToken" value={csrfToken} />
|
||||
<label
|
||||
@@ -134,7 +132,7 @@ export default function SigninPage (props: {
|
||||
<button type="submit">Sign in with {provider.name}</button>
|
||||
</form>
|
||||
)}
|
||||
{provider.type === 'credentials' && (
|
||||
{provider.type === "credentials" && (
|
||||
<form action={provider.callbackUrl} method="POST">
|
||||
<input type="hidden" name="csrfToken" value={csrfToken} />
|
||||
{Object.keys(provider.credentials).map((credential) => {
|
||||
@@ -149,9 +147,9 @@ export default function SigninPage (props: {
|
||||
<input
|
||||
name={credential}
|
||||
id={`input-${credential}-for-${provider.id}-provider`}
|
||||
type={provider.credentials[credential].type ?? 'text'}
|
||||
type={provider.credentials[credential].type ?? "text"}
|
||||
placeholder={
|
||||
provider.credentials[credential].placeholder ?? ''
|
||||
provider.credentials[credential].placeholder ?? ""
|
||||
}
|
||||
{...provider.credentials[credential]}
|
||||
/>
|
||||
@@ -161,7 +159,7 @@ export default function SigninPage (props: {
|
||||
<button type="submit">Sign in with {provider.name}</button>
|
||||
</form>
|
||||
)}
|
||||
{(provider.type === 'email' || provider.type === 'credentials') &&
|
||||
{(provider.type === "email" || provider.type === "credentials") &&
|
||||
i + 1 < providers.length && <hr />}
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export default `:root{--border-width:1px;--border-radius:0.5rem;--color-error:#c94b4b;--color-info:#157efb;--color-info-text:#fff}.__next-auth-theme-auto,.__next-auth-theme-light{--color-background:#fff;--color-text:#000;--color-primary:#444;--color-control-border:#bbb;--color-button-active-background:#f9f9f9;--color-button-active-border:#aaa;--color-seperator:#ccc}.__next-auth-theme-dark{--color-background:#000;--color-text:#fff;--color-primary:#ccc;--color-control-border:#555;--color-button-active-background:#060606;--color-button-active-border:#666;--color-seperator:#444}@media (prefers-color-scheme:dark){.__next-auth-theme-auto{--color-background:#000;--color-text:#fff;--color-primary:#ccc;--color-control-border:#555;--color-button-active-background:#060606;--color-button-active-border:#666;--color-seperator:#444}}body{background-color:var(--color-background);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;margin:0;padding:0}h1{font-weight:400;margin-bottom:1.5rem;padding:0 1rem}h1,p{color:var(--color-text)}form{margin:0;padding:0}label{font-weight:500;margin-bottom:.25rem;text-align:left}input[type],label{color:var(--color-text);display:block}input[type]{background:var(--color-background);border:var(--border-width) solid var(--color-control-border);border-radius:var(--border-radius);box-shadow:inset 0 .1rem .2rem rgba(0,0,0,.2);box-sizing:border-box;font-size:1rem;padding:.5rem 1rem;width:100%}input[type]:focus{box-shadow:none}p{font-size:1.1rem;line-height:2rem;margin:0 0 1.5rem;padding:0 1rem}a.button{line-height:1rem;text-decoration:none}a.button:link,a.button:visited{background-color:var(--color-background);color:var(--color-primary)}a.button,button{align-items:center;background-color:var(--provider-bg,var(--color-background));border-color:rgba(0,0,0,.1);border-radius:var(--border-radius);box-shadow:0 0 0 0 #000,0 0 0 0 #000,0 10px 15px -3px rgba(0,0,0,.2),0 4px 6px -4px rgba(0,0,0,.1);color:var(--provider-color,var(--color-primary));display:flex;font-size:1.1rem;font-weight:500;justify-content:center;margin:0 0 .75rem;min-height:62px;padding:.75rem 1rem;position:relative;transition:all .1s ease-in-out}a.button:has(img),button:has(img){justify-content:unset}a.button:has(img) span,button:has(img) span{flex-grow:1}a.button:hover,button:hover{cursor:pointer}a.button:active,button:active{box-shadow:0 .15rem .3rem rgba(0,0,0,.15),inset 0 .1rem .2rem var(--color-background),inset 0 -.1rem .1rem rgba(0,0,0,.1);cursor:pointer}a.button #provider-logo,button #provider-logo{display:block}a.button #provider-logo-dark,button #provider-logo-dark{display:none}@media (prefers-color-scheme:dark){a.button,button{background-color:var(--provider-dark-bg,var(--color-background));border:1px solid #0d0d0d;box-shadow:0 0 0 0 #000,0 0 0 0 #ccc,0 5px 5px -3px hsla(0,0%,100%,.01),0 4px 6px -4px hsla(0,0%,100%,.05);color:var(--provider-dark-color,var(--color-primary))}#provider-logo{display:none!important}#provider-logo-dark{display:block!important}}a.site{color:var(--color-primary);font-size:1rem;line-height:2rem;text-decoration:none}a.site:hover{text-decoration:underline}.page{display:grid;height:100%;margin:0;padding:0;place-items:center;position:absolute;width:100%}.page>div{padding:.5rem;text-align:center}.error a.button{display:inline-block;margin-top:.5rem;padding-left:2rem;padding-right:2rem}.error .message{margin-bottom:1.5rem}.signin input[type=text]{display:block;margin-left:auto;margin-right:auto}.signin hr{border:0;border-top:1px solid var(--color-seperator);display:block;margin:1.5em auto 0;overflow:visible}.signin hr:before{background:var(--color-background);color:#888;content:"or";padding:0 .4rem;position:relative;top:-.6rem}.signin .error{background:#f5f5f5;background:var(--color-info);border-radius:.3rem;font-weight:500}.signin .error p{color:var(--color-info-text);font-size:.9rem;line-height:1.2rem;padding:.5rem 1rem;text-align:left}.signin form,.signin>div{display:block}.signin form input[type],.signin>div input[type]{margin-bottom:.5rem}.signin form button,.signin>div button{width:100%}.signin form,.signin>div{max-width:300px}.signout .message{margin-bottom:1.5rem}.logo{display:inline-block;margin-top:100px;max-height:150px;max-width:300px}.card{border:1px solid var(--color-control-border);border-radius:5px;margin:50px auto;max-width:-moz-max-content;max-width:max-content;padding:20px 50px}.card .header{color:var(--color-primary)}.section-header{color:var(--brand-color,var(--color-text))}`
|
||||
// Generated by `pnpm css`
|
||||
// Generated by `pnpm css`
|
||||
|
||||
@@ -5,9 +5,13 @@ import { createHash } from "../web.js"
|
||||
import { handleAuthorized } from "./shared.js"
|
||||
|
||||
import type { AdapterSession } from "../../adapters.js"
|
||||
import type { RequestInternal, ResponseInternal, User } from "../../types.js"
|
||||
import type {
|
||||
RequestInternal,
|
||||
ResponseInternal,
|
||||
User,
|
||||
InternalOptions,
|
||||
} from "../../types.js"
|
||||
import type { Cookie, SessionStore } from "../cookie.js"
|
||||
import type { InternalOptions } from "../../types.js"
|
||||
|
||||
/** Handle callbacks from login services */
|
||||
export async function callback(params: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { AuthError } from '../../errors.js'
|
||||
import { AuthError } from "../../errors.js"
|
||||
|
||||
export type WarningCode = 'debug_enabled'
|
||||
export type WarningCode = "debug_enabled"
|
||||
|
||||
/**
|
||||
* Override any of the methods, and the rest will use the default logger.
|
||||
@@ -13,13 +13,13 @@ export interface LoggerInstance extends Record<string, Function> {
|
||||
debug: (message: string, metadata?: unknown) => void
|
||||
}
|
||||
|
||||
const red = '\x1b[31m'
|
||||
const yellow = '\x1b[33m'
|
||||
const grey = '\x1b[90m'
|
||||
const reset = '\x1b[0m'
|
||||
const red = "\x1b[31m"
|
||||
const yellow = "\x1b[33m"
|
||||
const grey = "\x1b[90m"
|
||||
const reset = "\x1b[0m"
|
||||
|
||||
export const logger: LoggerInstance = {
|
||||
error (error: AuthError) {
|
||||
error(error: AuthError) {
|
||||
const url = `https://errors.authjs.dev#${error.name.toLowerCase()}`
|
||||
console.error(error.stack)
|
||||
console.error(
|
||||
@@ -27,23 +27,23 @@ export const logger: LoggerInstance = {
|
||||
)
|
||||
error.metadata && console.error(JSON.stringify(error.metadata, null, 2))
|
||||
},
|
||||
warn (code) {
|
||||
warn(code) {
|
||||
const url = `https://errors.authjs.dev#${code}`
|
||||
console.warn(`${yellow}[auth][warn][${code}]${reset}`, `Read more: ${url}`)
|
||||
},
|
||||
debug (message, metadata) {
|
||||
debug(message, metadata) {
|
||||
console.log(
|
||||
`${grey}[auth][debug]:${reset} ${message}`,
|
||||
JSON.stringify(metadata, null, 2)
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the built-in logger with user's implementation.
|
||||
* Any `undefined` level will use the default logger.
|
||||
*/
|
||||
export function setLogger (
|
||||
export function setLogger(
|
||||
newLogger: Partial<LoggerInstance> = {},
|
||||
debug?: boolean
|
||||
) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { CommonProviderOptions } from "./index.js"
|
||||
import type { Awaitable, RequestInternal, User } from "../types.js"
|
||||
import type { Awaitable, User } from "../types.js"
|
||||
import type { JSXInternal } from "preact/src/jsx.js"
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Profile } from "../types.js"
|
||||
import CredentialsProvider from "./credentials.js"
|
||||
import type {
|
||||
default as CredentialsProvider,
|
||||
CredentialsConfig,
|
||||
CredentialsProviderType,
|
||||
} from "./credentials.js"
|
||||
|
||||
@@ -55,24 +55,24 @@
|
||||
* @module types
|
||||
*/
|
||||
|
||||
import type { CookieSerializeOptions } from 'cookie'
|
||||
import type { CookieSerializeOptions } from "cookie"
|
||||
import type {
|
||||
OAuth2TokenEndpointResponse,
|
||||
OpenIDTokenEndpointResponse
|
||||
} from 'oauth4webapi'
|
||||
import type { Adapter, AdapterUser } from './adapters.js'
|
||||
OpenIDTokenEndpointResponse,
|
||||
} from "oauth4webapi"
|
||||
import type { Adapter, AdapterUser } from "./adapters.js"
|
||||
import type {
|
||||
CredentialInput,
|
||||
CredentialsConfig,
|
||||
EmailConfig,
|
||||
OAuthConfigInternal,
|
||||
ProviderType
|
||||
} from './providers/index.js'
|
||||
import type { JWT, JWTOptions } from './jwt.js'
|
||||
import type { Cookie } from './lib/cookie.js'
|
||||
import type { LoggerInstance } from './lib/utils/logger.js'
|
||||
ProviderType,
|
||||
} from "./providers/index.js"
|
||||
import type { JWT, JWTOptions } from "./jwt.js"
|
||||
import type { Cookie } from "./lib/cookie.js"
|
||||
import type { LoggerInstance } from "./lib/utils/logger.js"
|
||||
|
||||
export type { AuthConfig } from './index.js'
|
||||
export type { AuthConfig } from "./index.js"
|
||||
export type Awaitable<T> = T | PromiseLike<T>
|
||||
export type { LoggerInstance }
|
||||
|
||||
@@ -83,7 +83,7 @@ export type { LoggerInstance }
|
||||
* [Pages](https://authjs.dev/guides/basics/pages)
|
||||
*/
|
||||
export interface Theme {
|
||||
colorScheme?: 'auto' | 'dark' | 'light'
|
||||
colorScheme?: "auto" | "dark" | "light"
|
||||
logo?: string
|
||||
brandColor?: string
|
||||
buttonText?: string
|
||||
@@ -95,7 +95,7 @@ export interface Theme {
|
||||
* but they refer to the same value.
|
||||
*/
|
||||
export type TokenSet = Partial<
|
||||
OAuth2TokenEndpointResponse | OpenIDTokenEndpointResponse
|
||||
OAuth2TokenEndpointResponse | OpenIDTokenEndpointResponse
|
||||
>
|
||||
|
||||
/**
|
||||
@@ -260,8 +260,8 @@ export interface EventCallbacks {
|
||||
*/
|
||||
signOut: (
|
||||
message:
|
||||
| { session: Awaited<ReturnType<Adapter['deleteSession']>> }
|
||||
| { token: Awaited<ReturnType<JWTOptions['decode']>> }
|
||||
| { session: Awaited<ReturnType<Adapter["deleteSession"]>> }
|
||||
| { token: Awaited<ReturnType<JWTOptions["decode"]>> }
|
||||
) => Awaitable<void>
|
||||
createUser: (message: { user: User }) => Awaitable<void>
|
||||
updateUser: (message: { user: User }) => Awaitable<void>
|
||||
@@ -276,26 +276,26 @@ export interface EventCallbacks {
|
||||
* - `token`: The JWT token for this session.
|
||||
* - `session`: The session object from your adapter.
|
||||
*/
|
||||
session: (message: { session: Session, token: JWT }) => Awaitable<void>
|
||||
session: (message: { session: Session; token: JWT }) => Awaitable<void>
|
||||
}
|
||||
|
||||
export type EventType = keyof EventCallbacks
|
||||
|
||||
/** TODO: Check if all these are used/correct */
|
||||
export type ErrorPageParam = 'Configuration' | 'AccessDenied' | 'Verification'
|
||||
export type ErrorPageParam = "Configuration" | "AccessDenied" | "Verification"
|
||||
|
||||
/** TODO: Check if all these are used/correct */
|
||||
export type SignInPageErrorParam =
|
||||
| 'Signin'
|
||||
| 'OAuthSignin'
|
||||
| 'OAuthCallback'
|
||||
| 'OAuthCreateAccount'
|
||||
| 'EmailCreateAccount'
|
||||
| 'Callback'
|
||||
| 'OAuthAccountNotLinked'
|
||||
| 'EmailSignin'
|
||||
| 'CredentialsSignin'
|
||||
| 'SessionRequired'
|
||||
| "Signin"
|
||||
| "OAuthSignin"
|
||||
| "OAuthCallback"
|
||||
| "OAuthCreateAccount"
|
||||
| "EmailCreateAccount"
|
||||
| "Callback"
|
||||
| "OAuthAccountNotLinked"
|
||||
| "EmailSignin"
|
||||
| "CredentialsSignin"
|
||||
| "SessionRequired"
|
||||
|
||||
export interface PagesOptions {
|
||||
/**
|
||||
@@ -344,7 +344,7 @@ export interface DefaultSession {
|
||||
*/
|
||||
export interface Session extends DefaultSession {}
|
||||
|
||||
export type SessionStrategy = 'jwt' | 'database'
|
||||
export type SessionStrategy = "jwt" | "database"
|
||||
|
||||
/** [Documentation](https://authjs.dev/reference/configuration/auth-config#session) */
|
||||
export interface SessionOptions {
|
||||
@@ -406,31 +406,31 @@ export interface User extends DefaultUser {}
|
||||
// Below are types that are only supposed be used by next-auth internally
|
||||
|
||||
/** @internal */
|
||||
export type InternalProvider<T = ProviderType> = (T extends 'oauth'
|
||||
export type InternalProvider<T = ProviderType> = (T extends "oauth"
|
||||
? OAuthConfigInternal<any>
|
||||
: T extends 'email'
|
||||
? EmailConfig
|
||||
: T extends 'credentials'
|
||||
? CredentialsConfig
|
||||
: never) & {
|
||||
signinUrl: string
|
||||
callbackUrl: string
|
||||
}
|
||||
: T extends "email"
|
||||
? EmailConfig
|
||||
: T extends "credentials"
|
||||
? CredentialsConfig
|
||||
: never) & {
|
||||
signinUrl: string
|
||||
callbackUrl: string
|
||||
}
|
||||
|
||||
export type AuthAction =
|
||||
| 'providers'
|
||||
| 'session'
|
||||
| 'csrf'
|
||||
| 'signin'
|
||||
| 'signout'
|
||||
| 'callback'
|
||||
| 'verify-request'
|
||||
| 'error'
|
||||
| "providers"
|
||||
| "session"
|
||||
| "csrf"
|
||||
| "signin"
|
||||
| "signout"
|
||||
| "callback"
|
||||
| "verify-request"
|
||||
| "error"
|
||||
|
||||
/** @internal */
|
||||
export interface RequestInternal {
|
||||
url: URL
|
||||
method: 'GET' | 'POST'
|
||||
method: "GET" | "POST"
|
||||
cookies?: Partial<Record<string, string>>
|
||||
headers?: Record<string, any>
|
||||
query?: Record<string, any>
|
||||
|
||||
@@ -5,14 +5,7 @@
|
||||
"emitDeclarationOnly": false
|
||||
},
|
||||
"watchOptions": {
|
||||
"excludeDirectories": [
|
||||
"jwt",
|
||||
"lib",
|
||||
"providers",
|
||||
],
|
||||
"excludeFiles": [
|
||||
"./*.d.ts",
|
||||
"./*.js"
|
||||
]
|
||||
"excludeDirectories": ["jwt", "lib", "providers"],
|
||||
"excludeFiles": ["./*.d.ts", "./*.js"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": [
|
||||
"./*.js",
|
||||
"./*.d.ts"
|
||||
]
|
||||
}
|
||||
"exclude": ["./*.js", "./*.d.ts"]
|
||||
}
|
||||
|
||||
@@ -6,11 +6,7 @@
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "preact",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
@@ -21,16 +17,8 @@
|
||||
"strictNullChecks": true,
|
||||
"stripInternal": true,
|
||||
"declarationMap": true,
|
||||
"declaration": true,
|
||||
"declaration": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
],
|
||||
"exclude": [
|
||||
"adapters.*",
|
||||
"index.*",
|
||||
"jwt",
|
||||
"lib",
|
||||
"providers",
|
||||
],
|
||||
}
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["adapters.*", "index.*", "jwt", "lib", "providers"]
|
||||
}
|
||||
|
||||
@@ -1,32 +1,105 @@
|
||||
/**
|
||||
*
|
||||
*
|
||||
* :::warning
|
||||
* `@auth/sveltekit` is currently experimental. The API _will_ change in the future.
|
||||
* :::
|
||||
*
|
||||
* SvelteKit Auth is the official SvelteKit integration for Auth.js.
|
||||
* It provides a simple way to add authentication to your SvelteKit app in a few lines of code.
|
||||
*
|
||||
*
|
||||
* ## Installation
|
||||
*
|
||||
* ```bash npm2yarn2pnpm
|
||||
* npm install @auth/core @auth/sveltekit
|
||||
* ```
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* ```ts title="src/hooks.server.ts"
|
||||
* 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 })],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* Don't forget to set the `AUTH_SECRET` [environment variable](https://kit.svelte.dev/docs/modules#$env-static-private). This should be a random 32 character string. On unix systems you can use `openssl rand -hex 32` or check out `https://generate-secret.vercel.app/32`.
|
||||
*
|
||||
* When deploying your app outside Vercel, set the `AUTH_TRUST_HOST` variable to `true` for other hosting providers like Cloudflare Pages or Netlify.
|
||||
*
|
||||
* The callback URL used by the [providers](https://authjs.dev/reference/core/modules/providers) must be set to the following, unless you override {@link SvelteKitAuthConfig.prefix}:
|
||||
* ```
|
||||
* [origin]/auth/callback/[provider]
|
||||
* ```
|
||||
*
|
||||
* ## Signing in and signing out
|
||||
*
|
||||
* ```ts
|
||||
* <script>
|
||||
* import { signIn, signOut } from "@auth/sveltekit/client"
|
||||
* import { page } from "$app/stores"
|
||||
* </script>
|
||||
*
|
||||
* <h1>SvelteKit Auth Example</h1>
|
||||
* <p>
|
||||
* {#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?.name ?? "User"}</strong>
|
||||
* </span>
|
||||
* <button on:click={() => signOut()} class="button">Sign out</button>
|
||||
* {:else}
|
||||
* <span class="notSignedInText">You are not signed in</span>
|
||||
* <button on:click={() => signIn("github")}>Sign In with GitHub</button>
|
||||
* {/if}
|
||||
* </p>
|
||||
* ```
|
||||
*
|
||||
* ## Notes
|
||||
*
|
||||
* :::info
|
||||
* Learn more about `@auth/sveltekit` [here](https://vercel.com/blog/announcing-sveltekit-auth).
|
||||
* :::
|
||||
*
|
||||
* :::info
|
||||
* PRs to improve this documentation are welcome! See [this file](https://github.com/nextauthjs/next-auth/blob/main/packages/frameworks-sveltekit/src/lib/index.ts).
|
||||
* :::
|
||||
*
|
||||
* @module main
|
||||
*/
|
||||
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
import type { Handle } from "@sveltejs/kit"
|
||||
|
||||
import { dev } from "$app/environment"
|
||||
import { env } from "$env/dynamic/private"
|
||||
import { AUTH_SECRET } from "$env/static/private"
|
||||
import {
|
||||
AuthHandler,
|
||||
type AuthAction,
|
||||
type AuthOptions,
|
||||
type Session,
|
||||
} from "@auth/core"
|
||||
import type { Handle } from "@sveltejs/kit"
|
||||
|
||||
export type GetSessionResult = Promise<Session | null>
|
||||
import { Auth } from "@auth/core"
|
||||
import type { AuthAction, AuthConfig, Session } from "@auth/core/types"
|
||||
|
||||
export async function getSession(
|
||||
req: Request,
|
||||
options: AuthOptions
|
||||
): GetSessionResult {
|
||||
options.secret ??= AUTH_SECRET
|
||||
options.trustHost ??= true
|
||||
config: AuthConfig
|
||||
): ReturnType<App.Locals["getSession"]> {
|
||||
config.secret ??= AUTH_SECRET
|
||||
config.trustHost ??= true
|
||||
|
||||
const url = new URL("/api/auth/session", req.url)
|
||||
const response = await AuthHandler(
|
||||
new Request(url, { headers: req.headers }),
|
||||
options
|
||||
)
|
||||
const request = new Request(url, { headers: req.headers })
|
||||
const response = await Auth(request, config)
|
||||
|
||||
const { status = 200 } = response
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
if (!data || !Object.keys(data).length) return null
|
||||
@@ -34,10 +107,14 @@ export async function getSession(
|
||||
throw new Error(data.message)
|
||||
}
|
||||
|
||||
export interface SvelteKitAuthOptions extends AuthOptions {
|
||||
/** Configure the {@link SvelteKitAuth} method. */
|
||||
export interface SvelteKitAuthConfig extends AuthConfig {
|
||||
/**
|
||||
* Defines the base path for the auth routes.
|
||||
* @default '/auth'
|
||||
* If you change the default value,
|
||||
* you must also update the callback URL used by the [providers](https://authjs.dev/reference/core/modules/providers).
|
||||
*
|
||||
* @default "/auth"
|
||||
*/
|
||||
prefix?: string
|
||||
}
|
||||
@@ -51,27 +128,23 @@ const actions: AuthAction[] = [
|
||||
"callback",
|
||||
"verify-request",
|
||||
"error",
|
||||
"_log",
|
||||
]
|
||||
|
||||
function SvelteKitAuthHandler(
|
||||
prefix: string,
|
||||
authOptions: AuthOptions
|
||||
): Handle {
|
||||
return ({ event, resolve }) => {
|
||||
function AuthHandle(prefix: string, authOptions: AuthConfig): Handle {
|
||||
return function ({ event, resolve }) {
|
||||
const { url, request } = event
|
||||
|
||||
event.locals.getSession ??= () => getSession(request, authOptions)
|
||||
|
||||
const [action] = url.pathname.slice(prefix.length + 1).split("/")
|
||||
if (
|
||||
actions.includes(action as AuthAction) &&
|
||||
url.pathname.startsWith(prefix + "/")
|
||||
) {
|
||||
return AuthHandler(request, authOptions)
|
||||
const action = url.pathname
|
||||
.slice(prefix.length + 1)
|
||||
.split("/")[0] as AuthAction
|
||||
|
||||
if (!actions.includes(action) || !url.pathname.startsWith(prefix + "/")) {
|
||||
return resolve(event)
|
||||
}
|
||||
|
||||
return resolve(event)
|
||||
return Auth(request, authOptions)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,19 +152,18 @@ function SvelteKitAuthHandler(
|
||||
* The main entry point to `@auth/sveltekit`
|
||||
* @see https://sveltekit.authjs.dev
|
||||
*/
|
||||
export default function SvelteKitAuth(options: SvelteKitAuthOptions): Handle {
|
||||
export function SvelteKitAuth(options: SvelteKitAuthConfig): Handle {
|
||||
const { prefix = "/auth", ...authOptions } = options
|
||||
authOptions.secret ??= AUTH_SECRET
|
||||
authOptions.trustHost ??= !!(env.AUTH_TRUST_HOST ?? env.VERCEL ?? dev)
|
||||
|
||||
return SvelteKitAuthHandler(prefix, authOptions)
|
||||
return AuthHandle(prefix, authOptions)
|
||||
}
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace App {
|
||||
interface Locals {
|
||||
getSession: () => GetSessionResult
|
||||
getSession(): Promise<Session | null>
|
||||
}
|
||||
interface PageData {
|
||||
session: Session | null
|
||||
|
||||
22
pnpm-lock.yaml
generated
22
pnpm-lock.yaml
generated
@@ -130,6 +130,8 @@ importers:
|
||||
|
||||
docs:
|
||||
specifiers:
|
||||
'@auth/core': workspace:*
|
||||
'@auth/sveltekit': workspace:*
|
||||
'@docusaurus/core': 2.2.0
|
||||
'@docusaurus/eslint-plugin': 2.2.0
|
||||
'@docusaurus/module-type-aliases': 2.2.0
|
||||
@@ -149,6 +151,8 @@ importers:
|
||||
remark-github: 10.1.0
|
||||
styled-components: 5.3.6
|
||||
dependencies:
|
||||
'@auth/core': link:../packages/core
|
||||
'@auth/sveltekit': link:../packages/frameworks-sveltekit
|
||||
'@mdx-js/react': 1.6.22_react@18.2.0
|
||||
'@sapphire/docusaurus-plugin-npm2yarn2pnpm': 1.1.4
|
||||
classnames: 2.3.2
|
||||
@@ -11085,8 +11089,10 @@ packages:
|
||||
indent-string: 4.0.0
|
||||
dev: true
|
||||
|
||||
/ajv-formats/2.1.1:
|
||||
/ajv-formats/2.1.1_ajv@8.11.0:
|
||||
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
|
||||
peerDependencies:
|
||||
ajv: ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
ajv:
|
||||
optional: true
|
||||
@@ -12942,8 +12948,8 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
is-text-path: 1.0.1
|
||||
JSONStream: 1.3.5
|
||||
is-text-path: 1.0.1
|
||||
lodash: 4.17.21
|
||||
meow: 8.1.2
|
||||
split2: 3.2.2
|
||||
@@ -15801,7 +15807,7 @@ packages:
|
||||
dependencies:
|
||||
'@apidevtools/json-schema-ref-parser': 9.0.9
|
||||
ajv: 8.11.0
|
||||
ajv-formats: 2.1.1
|
||||
ajv-formats: 2.1.1_ajv@8.11.0
|
||||
body-parser: 1.20.0
|
||||
content-type: 1.0.4
|
||||
deep-freeze: 0.0.1
|
||||
@@ -24661,6 +24667,12 @@ packages:
|
||||
/react-dev-utils/12.0.1_webpack@5.73.0:
|
||||
resolution: {integrity: sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
typescript: '>=2.7'
|
||||
webpack: '>=4'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.18.6
|
||||
address: 1.2.0
|
||||
@@ -24690,9 +24702,7 @@ packages:
|
||||
transitivePeerDependencies:
|
||||
- eslint
|
||||
- supports-color
|
||||
- typescript
|
||||
- vue-template-compiler
|
||||
- webpack
|
||||
dev: true
|
||||
|
||||
/react-dom/18.2.0_react@18.2.0:
|
||||
@@ -25535,7 +25545,7 @@ packages:
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.11
|
||||
ajv: 8.11.0
|
||||
ajv-formats: 2.1.1
|
||||
ajv-formats: 2.1.1_ajv@8.11.0
|
||||
ajv-keywords: 5.1.0_ajv@8.11.0
|
||||
dev: true
|
||||
|
||||
|
||||
Reference in New Issue
Block a user