Compare commits

..

27 Commits

Author SHA1 Message Date
Balázs Orbán
04791cd574 Merge branch 'main' into OrJDev/main 2022-12-31 10:52:24 +01:00
OrJDev
bc8353560d protected 2022-12-31 10:19:49 +02:00
OrJDev
2c1b85f456 mg 2022-12-31 10:08:12 +02:00
OrJDev
a83ace9770 Update Protected.tsx 2022-12-28 20:46:45 +02:00
OrJDev
74957b0e54 Update NavBar.tsx 2022-12-28 20:46:19 +02:00
OrJDev
10e06e0387 Update root.tsx 2022-12-28 19:54:12 +02:00
OrJDev
ebadffb179 upgrade auth example 2022-12-28 19:44:54 +02:00
OrJDev
9f50b6d7fc Merge branch 'nextauthjs:main' into main 2022-12-28 19:42:52 +02:00
OrJDev
3675bfae68 Merge branch 'nextauthjs:main' into main 2022-12-27 15:01:59 +02:00
OrJDev
966095c764 fix deps 2022-12-25 12:25:10 +02:00
OrJDev
d1973532fa Merge branch 'nextauthjs:main' into main 2022-12-25 12:17:44 +02:00
Nico Domino
0967daf9b9 Merge branch 'main' into main 2022-12-25 07:42:44 +01:00
OrJDev
35f5943a12 Merge branch 'main' into main 2022-12-24 18:50:39 +02:00
OrJDev
c0a74f80c5 minor fixes 2022-12-24 18:42:05 +02:00
OrJDev
f2dd22c666 Merge branch 'nextauthjs:main' into main 2022-12-24 18:38:48 +02:00
Balázs Orbán
54e06c01f2 Merge branch 'main' into main 2022-12-24 06:02:26 +01:00
OrJDev
7a000527f5 Merge branch 'main' into main 2022-12-23 21:28:34 +02:00
OrJDev
db8535f7c4 Merge branch 'main' into main 2022-12-23 13:44:27 +02:00
OrJDev
6af110cde7 Update sidebars.js 2022-12-23 08:36:50 +02:00
OrJDev
9d99b3d358 Merge branch 'main' into main 2022-12-23 08:24:40 +02:00
Balázs Orbán
8d75a619aa Merge branch 'main' into main 2022-12-23 07:07:04 +01:00
OrJDev
0ca53e4ec7 Update sync.yml 2022-12-23 07:39:09 +02:00
OrJDev
d7d3575b68 Fix sidebar typo 2022-12-23 07:35:13 +02:00
OrJDev
36ad964cf9 minor docs fixes 2022-12-23 07:33:21 +02:00
OrJDev
6fedd7f144 Update 02-oauth-tutorial.mdx 2022-12-23 07:16:07 +02:00
OrJDev
2dd5663a51 solidstart docs 2022-12-23 07:07:40 +02:00
OrJDev
1c5d022915 feat: add solid framework package and example 2022-12-23 06:52:41 +02:00
58 changed files with 8903 additions and 342 deletions

15
.github/sync.yml vendored
View File

@@ -7,15 +7,12 @@ nextauthjs/sveltekit-auth-example:
- .github/FUNDING.yml
- LICENSE
# FIXME: Should re-enable, but currently fails:
# https://github.com/nextauthjs/next-auth/actions/runs/3811709391/jobs/6484533340
# (issue seems to be the name of the target repo)
# nextauthjs/solid-start-auth-example:
# - source: "apps/examples/solid-start"
# dest: .
# deleteOrphaned: true
# - .github/FUNDING.yml
# - LICENSE
nextauthjs/solid-start-auth-example:
- source: apps/example-solid-start
dest: .
deleteOrphaned: true
- .github/FUNDING.yml
- LICENSE
nextauthjs/next-auth-gatsby-example:
- source: apps/playgrounds/gatsby

8711
apps/example-solid-start/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 664 B

After

Width:  |  Height:  |  Size: 664 B

View File

@@ -29,7 +29,7 @@ const Home: ParentComponent = () => {
</A>{" "}
with{" "}
<A
href="https://authjs.dev/reference/solidstart"
href="https://authjs.dev/reference/solid-start/modules/main"
class="text-blue-500 underline font-bold"
>
SolidStart Auth

View File

@@ -3,10 +3,9 @@ import { authOptions } from "./api/auth/[...nextauth]"
import Layout from "../components/layout"
import type { GetServerSidePropsContext } from "next"
import { useSession } from "next-auth/react"
import type { Session } from "next-auth"
export default function ServerSidePage() {
const { data: session } = useSession()
export default function ServerSidePage({ session }: { session: Session }) {
// As this page uses Server Side Rendering, the `session` will be already
// populated on render without needing to go through a loading stage.
return (

View File

@@ -2,203 +2,119 @@
title: Refresh token rotation
---
Refresh token rotation is the practice of updating an `access_token` on behalf of the user, without requiring interaction (eg.: re-sign in). `access_token`s are usually issued for a limited time. After they expire, the service verifying them will ignore the value. Instead of asking the user to sign in again to obtain a new `access_token`, certain providers support exchanging a `refresh_token` for a new `access_token`, renewing the expiry time. Let's see how this can be achieved.
While Auth.js doesn't automatically handle access token rotation for [OAuth providers](/reference/providers/oauth-builtin) yet, this functionality can be implemented using [callbacks](/guides/basics/callbacks).
:::note
Our goal is to add zero-config support for built-in providers eventually. Let us know if you would like to help.
:::
## Source Code
A working example can be accessed [here](https://github.com/nextauthjs/next-auth-refresh-token-example).
## Implementation
First, make sure that the provider you want to use supports `refresh_token`'s. Check out [The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749#section-6) spec for more details.
### Server Side
Depending on the session strategy, `refresh_token` can be persisted either in a database, or in a cookie, in an encrypted JWT.
:::info
Using a JWT to store the `refresh_token` is less secure than saving it in a database, and you need to evaluate based on your requirements which strategy you choose.
:::
#### JWT strategy
Using the [jwt](../../reference/03-core/interfaces/types.CallbacksOptions.md#jwt) and [session](../../reference/03-core/interfaces/types.CallbacksOptions.md#session) callbacks, we can persist OAuth tokens and refresh them when they expire.
Using a [JWT callback](https://authjs.dev/guides/basics/callbacks#jwt-callback) and a [session callback](https://authjs.dev/guides/basics/callbacks#session-callback), we can persist OAuth tokens and refresh them when they expire.
Below is a sample implementation using Google's Identity Provider. Please note that the OAuth 2.0 request in the `refreshAccessToken()` function will vary between different providers, but the core logic should remain similar.
```ts
import { Auth } from "@auth/core"
import { type TokenSet } from "@auth/core/types"
import Google from "@auth/core/providers/google"
```js title="pages/api/auth/[...nextauth].js"
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
export default Auth(new Request("https://example.com"), {
const GOOGLE_AUTHORIZATION_URL =
"https://accounts.google.com/o/oauth2/v2/auth?" +
new URLSearchParams({
prompt: "consent",
access_type: "offline",
response_type: "code",
})
/**
* Takes a token, and returns a new token with updated
* `accessToken` and `accessTokenExpires`. If an error occurs,
* returns the old token and an error property
*/
async function refreshAccessToken(token) {
try {
const url =
"https://oauth2.googleapis.com/token?" +
new URLSearchParams({
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
grant_type: "refresh_token",
refresh_token: token.refreshToken,
})
const response = await fetch(url, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
method: "POST",
})
const refreshedTokens = await response.json()
if (!response.ok) {
throw refreshedTokens
}
return {
...token,
accessToken: refreshedTokens.access_token,
accessTokenExpires: Date.now() + refreshedTokens.expires_at * 1000,
refreshToken: refreshedTokens.refresh_token ?? token.refreshToken, // Fall back to old refresh token
}
} catch (error) {
console.log(error)
return {
...token,
error: "RefreshAccessTokenError",
}
}
}
export default NextAuth({
providers: [
Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
authorization: { params: { access_type: "offline", prompt: "consent" } },
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
authorization: GOOGLE_AUTHORIZATION_URL,
}),
],
callbacks: {
async jwt({ token, account }) {
if (account) {
// Save the access token and refresh token in the JWT on the initial login
async jwt({ token, user, account }) {
// Initial sign in
if (account && user) {
return {
access_token: account.access_token,
expires_at: Date.now() + account.expires_in * 1000,
refresh_token: account.refresh_token,
}
} else if (Date.now() < token.expires_at) {
// If the access token has not expired yet, return it
return token
} else {
// If the access token has expired, try to refresh it
try {
// https://accounts.google.com/.well-known/openid-configuration
// We need the `token_endpoint`.
const response = await fetch("https://oauth2.googleapis.com/token", {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
client_id: process.env.GOOGLE_ID,
client_secret: process.env.GOOGLE_SECRET,
grant_type: "refresh_token",
refresh_token: token.refresh_token,
}),
method: "POST",
})
const tokens: TokenSet = await response.json()
if (!response.ok) throw tokens
return {
...token, // Keep the previous token properties
access_token: tokens.access_token,
expires_at: Date.now() + tokens.expires_in * 1000,
// Fall back to old refresh token, but note that
// many providers may only allow using a refresh token once.
refresh_token: tokens.refresh_token ?? token.refresh_token,
}
} catch (error) {
console.error("Error refreshing access token", error)
// The error property will be used client-side to handle the refresh token error
return { ...token, error: "RefreshAccessTokenError" as const }
accessToken: account.access_token,
accessTokenExpires: Date.now() + account.expires_at * 1000,
refreshToken: account.refresh_token,
user,
}
}
// Return previous token if the access token has not expired yet
if (Date.now() < token.accessTokenExpires) {
return token
}
// Access token has expired, try to update it
return refreshAccessToken(token)
},
async session({ session, token }) {
session.user = token.user
session.accessToken = token.accessToken
session.error = token.error
return session
},
},
})
declare module "@auth/core/types" {
interface Session {
error?: "RefreshAccessTokenError"
}
}
declare module "@auth/core/jwt" {
interface JWT {
access_token: string
expires_at: number
refresh_token: string
error?: "RefreshAccessTokenError"
}
}
```
#### Database strategy
Using the database strategy is very similar, but instead of preserving the `access_token` and `refresh_token`, we save it, well, in the database.
```ts
import { Auth } from "@auth/core"
import { type TokenSet } from "@auth/core/types"
import Google from "@auth/core/providers/google"
import { PrismaAdapter } from "@next-auth/prisma-adapter"
import { PrismaClient } from "@prisma/client"
const prisma = new PrismaClient()
export default Auth(new Request("https://example.com"), {
adapter: PrismaAdapter(prisma),
providers: [
Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
authorization: { params: { access_type: "offline", prompt: "consent" } },
}),
],
callbacks: {
async session({ session, user }) {
const [google] = await prisma.account.findMany({
where: { userId: user.id, provider: "google" },
})
if (google.expires_at >= Date.now()) {
// If the access token has expired, try to refresh it
try {
// https://accounts.google.com/.well-known/openid-configuration
// We need the `token_endpoint`.
const response = await fetch("https://oauth2.googleapis.com/token", {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
client_id: process.env.GOOGLE_ID,
client_secret: process.env.GOOGLE_SECRET,
grant_type: "refresh_token",
refresh_token: google.refresh_token,
}),
method: "POST",
})
const tokens: TokenSet = await response.json()
if (!response.ok) throw tokens
await prisma.account.update({
data: {
access_token: tokens.access_token,
expires_at: Date.now() + tokens.expires_in * 1000,
refresh_token: tokens.refresh_token ?? google.refresh_token,
},
where: {
provider_providerAccountId: {
provider: "google",
providerAccountId: google.providerAccountId,
},
},
})
} catch (error) {
console.error("Error refreshing access token", error)
// The error property will be used client-side to handle the refresh token error
session.error = "RefreshAccessTokenError"
}
}
return session
},
},
})
declare module "@auth/core/types" {
interface Session {
error?: "RefreshAccessTokenError"
}
}
declare module "@auth/core/jwt" {
interface JWT {
access_token: string
expires_at: number
refresh_token: string
error?: "RefreshAccessTokenError"
}
}
```
### Client Side
The `RefreshAccessTokenError` error that is caught in the `refreshAccessToken()` method is passed to the client. This means that you can direct the user to the sign-in flow if we cannot refresh their token.
The `RefreshAccessTokenError` error that is caught in the `refreshAccessToken()` method is passed all the way to the client. This means that you can direct the user to the sign in flow if we cannot refresh their token.
We can handle this functionality as a side effect:
@@ -218,8 +134,3 @@ const HomePage() {
return (...)
}
```
## Source Code
A working example can be accessed [here](https://github.com/nextauthjs/next-auth-refresh-token-example).

View File

@@ -1,14 +1,14 @@
---
title: Available OAuth providers
sidebar_label: OAuth providers
sidebar_label: Oauth providers
---
Authentication Providers in **Auth.js** are services that can be used to sign a user in.
Authentication Providers in **Auth.js** are services that can be used to sign in a user.
Auth.js comes with a set of built-in providers. You can find them [here](https://github.com/nextauthjs/next-auth/tree/main/packages/core/src/providers). Each built-in provider has its own documentation page:
:::note
Auth.js supports any **2.x** and **OpenID Connect (OIDC)** compliant providers and has built-in support for the most popular services.
Auth.js is designed to work with any OAuth service, it supports **OAuth 1.0**, **1.0A**, **2.0** and **OpenID Connect (OIDC)** and has built-in support for most popular sign-in services.
:::
<ul>

View File

@@ -11,7 +11,7 @@ http://developers.strava.com/docs/reference/
The **Strava Provider** comes with a set of default options:
- [Strava Provider options](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/providers/strava.ts)
- [Strava Provider options](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/providers/strava.js)
You can override any of the options to suit your own use case.

View File

@@ -3,7 +3,7 @@ id: dynamodb
title: DynamoDB
---
This is the AWS DynamoDB Adapter for `next-auth`. This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package.
This is the AWS DynamoDB Adapter for next-auth. This package can only be used in conjunction with the primary next-auth package. It is not a standalone package.
By default, the adapter expects a table with a partition key `pk` and a sort key `sk`, as well as a global secondary index named `GSI1` with `GSI1PK` as partition key and `GSI1SK` as sorting key. To automatically delete sessions and verification requests after they expire using [dynamodb TTL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html) you should [enable the TTL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/time-to-live-ttl-how-to.html) with attribute name 'expires'. You can set whatever you want as the table name and the billing method.
@@ -11,10 +11,10 @@ You can find the full schema in the table structure section below.
## Getting Started
1. Install `next-auth`, `@next-auth/dynamodb-adapter`, `@aws-sdk/client-dynamodb` and `@aws-sdk/lib-dynamodb`
1. Install `next-auth` and `@next-auth/dynamodb-adapter`
```bash npm2yarn2pnpm
npm install next-auth @next-auth/dynamodb-adapter @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
```bash npm2yarn
npm install next-auth @next-auth/dynamodb-adapter
```
2. Add this adapter to your `pages/api/auth/[...nextauth].js` next-auth configuration object.
@@ -23,7 +23,7 @@ You need to pass `DynamoDBDocument` client from the modular [`aws-sdk`](https://
The default table name is `next-auth`, but you can customise that by passing `{ tableName: 'your-table-name' }` as the second parameter in the adapter.
```javascript title="pages/api/auth/[...nextauth].js"
import { DynamoDB, DynamoDBClientConfig } from "@aws-sdk/client-dynamodb"
import { DynamoDB } from "@aws-sdk/client-dynamodb"
import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb"
import NextAuth from "next-auth";
import Providers from "next-auth/providers";
@@ -73,7 +73,7 @@ The table respects the single table design pattern. This has many advantages:
- Only one table to manage, monitor and provision.
- Querying relations is faster than with multi-table schemas (for eg. retrieving all sessions for a user).
- Only one table needs to be replicated if you want to go multi-region.
- Only one table needs to be replicated, if you want to go multi-region.
> This schema is adapted for use in DynamoDB and based upon our main [schema](/reference/adapters/models)
@@ -94,7 +94,7 @@ new dynamodb.Table(this, `NextAuthTable`, {
})
```
Alternatively, you can use this cloudformation template:
Alternatively you can use this cloudformation template:
```yaml title=cloudformation.yaml
NextAuthTable:

View File

@@ -46,7 +46,7 @@ const docusaurusConfig = {
title: "Auth.js",
logo: {
alt: "Auth.js Logo",
src: "img/logo/logo-xs.webp",
src: "img/logo/logo-xs.png",
},
items: [
{
@@ -101,7 +101,7 @@ const docusaurusConfig = {
announcementBar: {
id: "new-major-announcement",
content:
"<a target='_blank' rel='noopener noreferrer' href='https://next-auth.js.org'>NextAuth.js</a> is becoming Auth.js! 🎉 We're creating Authentication for the Web. Everyone included. Starting with SvelteKit, check out <a href='/reference/sveltekit'>the docs</a>.",
"<a target='_blank' rel='noopener noreferrer' href='https://next-auth.js.org'>NextAuth.js</a> is becoming Auth.js! 🎉 We're creating Authentication for the Web. Everyone included. Starting with SvelteKit, check out the docs <a href='/reference/sveltekit'>here</a>.",
backgroundColor: "#000",
textColor: "#fff",
},
@@ -121,7 +121,6 @@ const docusaurusConfig = {
alt="Powered by Vercel"
style="margin-top: 8px"
height="32"
width="167"
src="https://raw.githubusercontent.com/nextauthjs/next-auth/main/docs/static/img/powered-by-vercel.svg"
/>
</a>`,
@@ -182,10 +181,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",
@@ -205,15 +201,7 @@ const docusaurusConfig = {
...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,
@@ -226,9 +214,7 @@ const docusaurusConfig = {
...typedocConfig,
id: "sveltekit",
plugin: ["./tyepdoc"],
entryPoints: ["index.ts", "client.ts"].map(
(e) => `../packages/frameworks-sveltekit/src/lib/${e}`
),
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,

View File

@@ -22,7 +22,6 @@
"classnames": "^2.3.2",
"mdx-mermaid": "1.2.2",
"mermaid": "9.0.1",
"next-auth": "workspace:*",
"prism-react-renderer": "1.3.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@@ -60,7 +60,7 @@ module.exports = {
type: "doc",
id: "reference/solidstart/index",
},
items: ["reference/solidstart/client", "reference/solidstart/protected"],
items: ["reference/solidstart/client"],
},
{
type: "category",

View File

@@ -140,19 +140,19 @@ html[data-theme="dark"] hr {
border-radius: 10rem;
overflow: visible;
box-shadow: 0 0 2rem rgba(0, 0, 0, 0.1);
background-image: url("/img/mesh-1.webp");
background-image: url("/img/mesh-1.jpg");
background-size: cover;
background-origin: center;
}
.home-main .section-features .row .col:nth-child(2) .feature-image-wrapper {
background-image: url("/img/mesh-2.webp");
background-image: url("/img/mesh-2.jpg");
background-size: cover;
background-origin: center;
}
.home-main .section-features .row .col:nth-child(3) .feature-image-wrapper {
background-image: url("/img/mesh-3.webp");
background-image: url("/img/mesh-3.jpg");
background-size: cover;
background-origin: center;
}

View File

@@ -6,11 +6,6 @@
margin-right: 1rem !important;
}
.navbar__logo {
width: 29px;
height: 32px;
}
.navbar__title {
font-size: 1.2rem;
margin-left: 0.2rem;

View File

@@ -117,11 +117,9 @@ export default function Home() {
<div className="container">
<div className="hero-inner">
<img
src="/img/logo/logo-sm.webp"
src="/img/logo/logo-sm.png"
alt="Shield with key icon"
className={styles.heroLogo}
height="142"
width="128"
/>
<div className={styles.heroText}>
<h1 className="hero__title">{siteConfig.title}</h1>
@@ -216,9 +214,9 @@ export default function Home() {
<div className="row">
<div className="col col--6">
<div className="code">
<div className="code-heading">
<h4 className="code-heading">
Next.js <span>/pages/api/auth/[...nextauth].ts</span>
</div>
</h4>
<CodeBlock className="prism-code language-js">
{nextJsCode}
</CodeBlock>
@@ -226,9 +224,9 @@ export default function Home() {
</div>
<div className="col col--6">
<div className="code">
<div className="code-heading">
<h4 className="code-heading">
SvelteKit <span>/hooks.server.ts</span>
</div>
</h4>
<CodeBlock className="prism-code language-js">
{svelteKitCode}
</CodeBlock>
@@ -236,9 +234,9 @@ export default function Home() {
</div>
<div className="col col--6">
<div className="code">
<div className="code-heading">
<h4 className="code-heading">
SolidStart <span>/routes/api/auth/[...solidauth].ts</span>
</div>
</h4>
<CodeBlock className="prism-code language-js">
{solidStartCode}
</CodeBlock>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -60,11 +60,6 @@
"destination": "https://github.com/nextauthjs/next-auth/discussions/categories/questions",
"permanent": true
},
{
"source": "/reference/solid-start/:path*",
"destination": "/reference/solidstart/:path*",
"permanent": true
},
{
"source": "/",
"has": [
@@ -75,16 +70,6 @@
],
"destination": "https://authjs.dev/reference/sveltekit/modules/main"
},
{
"source": "/",
"has": [
{
"type": "host",
"value": "solid-start.authjs.dev"
}
],
"destination": "https://authjs.dev/reference/solid-start"
},
{
"source": "/:path(.*)",
"has": [

View File

@@ -5,7 +5,6 @@
"repository": "https://github.com/nextauthjs/next-auth.git",
"scripts": {
"build:app": "turbo run build --filter=next-auth-app",
"build:docs": "turbo run build --filter=docs",
"build": "turbo run build --filter=next-auth --filter=@next-auth/* --filter=@auth/* --no-deps",
"test": "turbo run test --concurrency=1 --filter=[HEAD^1] --filter=./packages/* --filter=!*pouchdb-* --filter=!@*upstash* --filter=!*dynamodb-*",
"clean": "turbo run clean --no-cache",

View File

@@ -1,7 +1,7 @@
{
"name": "@next-auth/dynamodb-adapter",
"repository": "https://github.com/nextauthjs/next-auth",
"version": "1.0.6",
"version": "1.0.5",
"description": "AWS DynamoDB adapter for next-auth.",
"keywords": [
"next-auth",
@@ -31,7 +31,6 @@
"author": "Pol Marnette",
"license": "ISC",
"peerDependencies": {
"@aws-sdk/client-dynamodb": "^3.36.1",
"@aws-sdk/lib-dynamodb": "^3.36.1",
"next-auth": "^4"
},
@@ -44,4 +43,4 @@
"jest": "^27.4.3",
"next-auth": "workspace:*"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@auth/core",
"version": "0.2.5",
"version": "0.2.3",
"description": "Authentication for the Web.",
"keywords": [
"authentication",
@@ -20,7 +20,7 @@
"Balázs Orbán <info@balazsorban.com>",
"Nico Domino <yo@ndo.dev>",
"Lluis Agusti <hi@llu.lu>",
"Thang Huu Vu <hi@thvu.dev>",
"Thang Huu Vu <thvu@hey.com>",
"Iain Collins <me@iaincollins.com"
],
"type": "module",
@@ -61,10 +61,10 @@
},
"license": "ISC",
"dependencies": {
"@panva/hkdf": "^1.0.2",
"@panva/hkdf": "1.0.2",
"cookie": "0.5.0",
"jose": "^4.11.1",
"oauth4webapi": "^2.0.6",
"jose": "4.11.1",
"oauth4webapi": "2.0.6",
"preact": "10.11.3",
"preact-render-to-string": "5.2.3"
},
@@ -92,4 +92,4 @@
"postcss": "8.4.19",
"postcss-nested": "6.0.0"
}
}
}

View File

@@ -22,8 +22,7 @@ export async function getAuthorizationUrl(
let url = provider.authorization?.url
let as: o.AuthorizationServer | undefined
// Falls back to authjs.dev if the user only passed params
if (!url || url.host === "authjs.dev") {
if (!url) {
// If url is undefined, we assume that issuer is always defined
// We check this in assert.ts
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -49,9 +48,9 @@ export async function getAuthorizationUrl(
redirect_uri: provider.callbackUrl,
// @ts-expect-error TODO:
...provider.authorization?.params,
},
Object.fromEntries(provider.authorization?.url.searchParams ?? []),
query
}, // Defaults
Object.fromEntries(authParams), // From provider config
query // From `signIn` call
)
for (const k in params) authParams.set(k, params[k])

View File

@@ -31,12 +31,7 @@ export async function handleOAuth(
const { logger, provider } = options
let as: o.AuthorizationServer
const { token, userinfo } = provider
// Falls back to authjs.dev if the user only passed params
if (
(!token?.url || token.url.host === "authjs.dev") &&
(!userinfo?.url || userinfo.url.host === "authjs.dev")
) {
if (!provider.token?.url && !provider.userinfo?.url) {
// We assume that issuer is always defined as this has been asserted earlier
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const issuer = new URL(provider.issuer!)
@@ -59,9 +54,9 @@ export async function handleOAuth(
as = discoveredAs
} else {
as = {
issuer: provider.issuer ?? "https://authjs.dev", // TODO: review fallback issuer
token_endpoint: token?.url.toString(),
userinfo_endpoint: userinfo?.url.toString(),
issuer: provider.issuer ?? "https://a", // TODO: review fallback issuer
token_endpoint: provider.token?.url.toString(),
userinfo_endpoint: provider.userinfo?.url.toString(),
}
}
@@ -148,9 +143,9 @@ export async function handleOAuth(
throw new Error("TODO: Handle OAuth 2.0 response body error")
}
if (userinfo?.request) {
profile = await userinfo.request({ tokens, provider })
} else if (userinfo?.url) {
if (provider.userinfo?.request) {
profile = await provider.userinfo.request({ tokens, provider })
} else if (provider.userinfo?.url) {
const userinfoResponse = await o.userInfoRequest(
as,
client,

View File

@@ -45,11 +45,11 @@ export default function parseProviders(params: {
}
}
// TODO: Also add discovery here, if some endpoints/config are missing.
// We should return both a client and authorization server config.
function normalizeOAuth(
c: OAuthConfig<any> | OAuthUserConfig<any>
c?: OAuthConfig<any> | OAuthUserConfig<any>
): OAuthConfigInternal<any> | {} {
if (!c) return {}
if (c.issuer) c.wellKnown ??= `${c.issuer}/.well-known/openid-configuration`
const authorization = normalizeEndpoint(c.authorization, c.issuer)
@@ -84,18 +84,18 @@ function normalizeEndpoint(
e?: OAuthConfig<any>[OAuthEndpointType],
issuer?: string
): OAuthConfigInternal<any>[OAuthEndpointType] {
if (!e && issuer) return
if (!e || issuer) return
if (typeof e === "string") {
return { url: new URL(e) }
}
// If e.url is undefined, it's because the provider config
// If v.url is undefined, it's because the provider config
// assumes that we will use the issuer endpoint.
// The existence of either e.url or provider.issuer is checked in
// assert.ts. We fallback to "https://authjs.dev" to be able to pass around
// a valid URL even if the user only provided params.
// NOTE: This need to be checked when constructing the URL
// for the authorization, token and userinfo endpoints.
const url = new URL(e?.url ?? "https://authjs.dev")
for (const k in e?.params) url.searchParams.set(k, e?.params[k])
return { url, request: e?.request }
// The existence of either v.url or provider.issuer is checked in
// assert.ts
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const url = new URL(e.url!)
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
for (const k in e.params) url.searchParams.set(k, e.params[k] as any)
return { ...e, url }
}

View File

@@ -2,7 +2,7 @@ import { handleLogin } from "../callback-handler.js"
import { CallbackRouteError } from "../../errors.js"
import { handleOAuth } from "../oauth/callback.js"
import { createHash } from "../web.js"
import { getAdapterUserFromEmail, handleAuthorized } from "./shared.js"
import { handleAuthorized } from "./shared.js"
import type { AdapterSession } from "../../adapters.js"
import type {
@@ -10,7 +10,6 @@ import type {
ResponseInternal,
User,
InternalOptions,
Account,
} from "../../types.js"
import type { Cookie, SessionStore } from "../cookie.js"
@@ -173,40 +172,40 @@ export async function callback(params: {
}
// @ts-expect-error -- Verified in `assertConfig`.
const user = await getAdapterUserFromEmail(identifier, adapter)
const profile = await getAdapterUserFromEmail(identifier, adapter)
const account: Account = {
providerAccountId: user.email,
userId: user.id,
const account = {
providerAccountId: profile.email,
type: "email" as const,
provider: provider.id,
}
// Check if user is allowed to sign in
const unauthorizedOrError = await handleAuthorized(
{ user, account },
{ user: profile, account },
options
)
if (unauthorizedOrError) return { ...unauthorizedOrError, cookies }
// Sign user in
const {
user: loggedInUser,
session,
isNewUser,
} = await handleLogin(sessionStore.value, user, account, options)
const { user, session, isNewUser } = await handleLogin(
sessionStore.value,
profile,
account,
options
)
if (useJwtSession) {
const defaultToken = {
name: loggedInUser.name,
email: loggedInUser.email,
picture: loggedInUser.image,
sub: loggedInUser.id?.toString(),
name: user.name,
email: user.email,
picture: user.image,
sub: user.id?.toString(),
}
const token = await callbacks.jwt({
token: defaultToken,
user: loggedInUser,
user,
account,
isNewUser,
})
@@ -234,7 +233,7 @@ export async function callback(params: {
})
}
await events.signIn?.({ user: loggedInUser, account, isNewUser })
await events.signIn?.({ user, account, isNewUser })
// Handle first logins on new accounts
// e.g. option to send users to a new account landing page on initial login

View File

@@ -33,7 +33,7 @@ export async function signin(
const account: Account = {
providerAccountId: email,
userId: user.id,
userId: email,
type: "email",
provider: provider.id,
}

View File

@@ -1,7 +1,7 @@
{
"name": "@auth/solid-start",
"description": "Authentication for SolidStart.",
"version": "0.1.0",
"version": "0.0.1",
"type": "module",
"files": [
"client.*",
@@ -55,4 +55,4 @@
"author": "OrJDev <orjdeveloper@gmail.com>",
"repository": "https://github.com/nextauthjs/next-auth",
"license": "ISC"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@auth/sveltekit",
"version": "0.1.12",
"version": "0.1.10",
"description": "Authentication for SvelteKit.",
"keywords": [
"authentication",
@@ -69,4 +69,4 @@
},
"./package.json": "./package.json"
}
}
}

View File

@@ -18,7 +18,7 @@
* ## Usage
*
* ```ts title="src/hooks.server.ts"
* import { SvelteKitAuth } from "@auth/sveltekit"
* import SvelteKitAuth from "@auth/sveltekit"
* import GitHub from "@auth/core/providers/github"
* import { GITHUB_ID, GITHUB_SECRET } from "$env/static/private"
*

View File

@@ -9,7 +9,7 @@
"Balázs Orbán <info@balazsorban.com>",
"Nico Domino <yo@ndo.dev>",
"Lluis Agusti <hi@llu.lu>",
"Thang Huu Vu <hi@thvu.dev>"
"Thang Huu Vu <thvu@hey.com>"
],
"main": "index.js",
"module": "index.js",

22
pnpm-lock.yaml generated
View File

@@ -258,7 +258,6 @@ importers:
docusaurus-plugin-typedoc: ^0.18.0
mdx-mermaid: 1.2.2
mermaid: 9.0.1
next-auth: workspace:*
prism-react-renderer: 1.3.5
react: ^18.2.0
react-dom: ^18.2.0
@@ -273,7 +272,6 @@ importers:
classnames: 2.3.2
mdx-mermaid: 1.2.2_mermaid@9.0.1+react@18.2.0
mermaid: 9.0.1
next-auth: link:../packages/next-auth
prism-react-renderer: 1.3.5_react@18.2.0
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
@@ -579,15 +577,15 @@ importers:
packages/core:
specifiers:
'@next-auth/tsconfig': workspace:*
'@panva/hkdf': ^1.0.2
'@panva/hkdf': 1.0.2
'@types/cookie': 0.5.1
'@types/node': 18.11.10
'@types/nodemailer': 6.4.6
'@types/react': 18.0.26
autoprefixer: 10.4.13
cookie: 0.5.0
jose: ^4.11.1
oauth4webapi: ^2.0.6
jose: 4.11.1
oauth4webapi: 2.0.6
postcss: 8.4.19
postcss-nested: 6.0.0
preact: 10.11.3
@@ -13095,8 +13093,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
@@ -13630,7 +13630,7 @@ packages:
/axios/0.25.0_debug@4.3.4:
resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==}
dependencies:
follow-redirects: 1.15.1
follow-redirects: 1.15.1_debug@4.3.4
transitivePeerDependencies:
- debug
@@ -18690,7 +18690,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
@@ -19296,6 +19296,7 @@ packages:
peerDependenciesMeta:
debug:
optional: true
dev: true
/follow-redirects/1.15.1_debug@3.2.7:
resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==}
@@ -19319,7 +19320,6 @@ packages:
optional: true
dependencies:
debug: 4.3.4
dev: true
/for-each/0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
@@ -21106,7 +21106,7 @@ packages:
engines: {node: '>=8.0.0'}
dependencies:
eventemitter3: 4.0.7
follow-redirects: 1.15.1
follow-redirects: 1.15.1_debug@4.3.4
requires-port: 1.0.0
transitivePeerDependencies:
- debug
@@ -29489,7 +29489,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

View File

@@ -1,25 +1,14 @@
{
"$schema": "https://turborepo.org/schema.json",
"pipeline": {
"docs#build": {
"dependsOn": ["^build", "next-auth#build"]
},
"build": {
"dependsOn": ["^build"]
},
"next-auth#build": {
"dependsOn": ["^build"],
"outputs": [
"client/**",
"core/**",
"css/**",
"jwt/**",
"next/**",
"providers/**",
"react/**",
"index.d.ts",
"index.js",
"adapters.d.ts",
"middleware.d.ts",
"middleware.js"
]
"dependsOn": ["^build"]
},
"clean": {
"cache": false