Compare commits

...

15 Commits

Author SHA1 Message Date
Balázs Orbán
ac5d8a9795 chore(release): bump package version(s) [skip ci] 2022-09-25 11:42:17 +02:00
Matt Oliver
965c6267e2 feat(core): make session token with DB session strategy customizable (#5328)
* Add option for custom generateSessionToken

* Apply suggestions from code review

* Apply suggestions from code review

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2022-09-25 10:26:59 +01:00
Sébastien Vanvelthem
bfc429d20b fix: update jose to fix nextjs edge error with middleware (#5372)
fix: update jose to fix nextjs edge error
2022-09-25 15:46:02 +07:00
Balázs Orbán
2d8e910a19 chore(release): bump package version(s) [skip ci] 2022-09-25 10:29:56 +02:00
voinik
d16e04848e fix(adapters): check token during email verification in Upstash Adapter (#5377)
* Check token during email verification

* Undo accidental linter fix

* Update index.ts

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2022-09-25 09:10:55 +01:00
Balázs Orbán
ff3a52895b chore(release): bump package version(s) [skip ci] 2022-09-25 09:42:51 +02:00
Balázs Orbán
e6e03e8842 feat(adapters): update Mikro ORM adapter schema
BREAKING CHANGE:

See https://github.com/nextauthjs/next-auth/pull/5316
2022-09-25 09:29:17 +02:00
Thomas Large
715aad9474 chore: Add Next to peerDeps & bump to 12.2.5 in devDeps (#5384) 2022-09-24 13:39:07 +07:00
Jonas Strassel
902bf92a85 fix(mikro-orm): re-enable tests (#5316) 2022-09-21 23:36:23 +07:00
Eng Zer Jun
44f2a47e6e fix(middleware): use includes() for NextAuth pages (#5104)
* fix(middleware): use `includes()` for NextAuth pages

Some users could be setting their `signIn` and `error` pages option to
`/` to disable the automatically generated pages, as suggested in [1].

This commit reverts the behaviour for matching `signIn` and `error`
pages in `handleMiddleware` to pre-v4.10.3.

```
const signInPage = "/"
const errorPage = "/"
const publicPaths = [signInPage, errorPage, "/_next", "/favicon.ico"]

// pathname = "/" will return true
publicPaths.some((p) => pathname.startsWith(p))
```

Fixes: aedabc8d ("fix: avoid redirect on always public paths")
Reference [1]: https://github.com/nextauthjs/next-auth/discussions/2330#discussioncomment-1678298
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>

* test(middleware): add tests for public paths

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
Co-authored-by: Thang Vu <thvu@hey.com>
2022-09-18 11:07:46 +07:00
dependabot[bot]
a3b92dbaec chore(deps): bump jose from 4.5.0 to 4.9.3 in /apps/playground-sveltekit (#5359)
Bumps [jose](https://github.com/panva/jose) from 4.5.0 to 4.9.3.
- [Release notes](https://github.com/panva/jose/releases)
- [Changelog](https://github.com/panva/jose/blob/main/CHANGELOG.md)
- [Commits](https://github.com/panva/jose/compare/v4.5.0...v4.9.3)

---
updated-dependencies:
- dependency-name: jose
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-17 01:55:54 +02:00
Steve Burtenshaw
bdd3ab2816 docs(middleware): remove reference to nested (#5355)
Fixes #5180
2022-09-16 10:30:58 +02:00
Dulmandakh
ba55f06585 chore(deps): bump cookie to 0.5.0 (#5339) 2022-09-14 18:31:00 +02:00
Steve Burtenshaw
d2b877fb28 docs(client): onUnauthenticated reference (#5340) 2022-09-14 17:26:02 +02:00
Yuriy Gromchenko
658b22d9fb docs(atlassian): update provider scope (#5337) 2022-09-14 17:23:01 +02:00
25 changed files with 1926 additions and 272 deletions

View File

@@ -1161,9 +1161,9 @@ isexe@^2.0.0:
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
jose@^4.1.4, jose@^4.3.7:
version "4.5.0"
resolved "https://registry.yarnpkg.com/jose/-/jose-4.5.0.tgz#92829d8cf846351eb55aaaf94f252fb1d191f2d5"
integrity sha512-GFcVFQwYQKbQTUOo2JlpFGXTkgBw26uzDsRMD2q1WgSKNSnpKS9Ug7bdQ8dS+p4sZHNH6iRPu6WK2jLIjspaMA==
version "4.9.3"
resolved "https://registry.yarnpkg.com/jose/-/jose-4.9.3.tgz#890abd3f26725fe0f2aa720bc2f7835702b624db"
integrity sha512-f8E/z+T3Q0kA9txzH2DKvH/ds2uggcw0m3vVPSB9HrSkrQ7mojjifvS7aR8cw+lQl2Fcmx9npwaHpM/M3GD8UQ==
js-yaml@^4.1.0:
version "4.1.0"

View File

@@ -114,6 +114,12 @@ session: {
// Use it to limit write operations. Set to 0 to always update the database.
// Note: This option is ignored if using JSON Web Tokens
updateAge: 24 * 60 * 60, // 24 hours
// The session token is usually either a random UUID or string, however if you
// need a more customized session token string, you can define your own generate function.
generateSessionToken: () => {
return randomUUID?.() ?? randomBytes(32).toString("hex")
}
}
```

View File

@@ -67,7 +67,7 @@ export default function Component() {
Due to the way how Next.js handles `getServerSideProps` and `getInitialProps`, every protected page load has to make a server-side request to check if the session is valid and then generate the requested page (SSR). This increases server load, and if you are good with making the requests from the client, there is an alternative. You can use `useSession` in a way that makes sure you always have a valid session. If after the initial loading state there was no session found, you can define the appropriate action to respond.
The default behavior is to redirect the user to the sign-in page, from where - after a successful login - they will be sent back to the page they started on. You can also define an `onFail()` callback, if you would like to do something else:
The default behavior is to redirect the user to the sign-in page, from where - after a successful login - they will be sent back to the page they started on. You can also define an `onUnauthenticated()` callback, if you would like to do something else:
#### Example

View File

@@ -24,7 +24,11 @@ providers: [
AtlassianProvider({
clientId: process.env.ATLASSIAN_CLIENT_ID,
clientSecret: process.env.ATLASSIAN_CLIENT_SECRET,
scope: "write:jira-work read:jira-work read:jira-user offline_access read:me"
authorization: {
params: {
scope: "write:jira-work read:jira-work read:jira-user offline_access read:me"
}
}
})
]
...

View File

@@ -42,13 +42,19 @@ export default function Page() {
### Next.js (Middleware)
With NextAuth.js 4.2.0 and Next.js 12, you can now protect your pages via the middleware pattern more easily. If you would like to protect all pages, you can create a `_middleware.js` file in your root `pages` directory which looks like this.
With NextAuth.js 4.2.0 and Next.js 12, you can now protect your pages via the middleware pattern more easily. If you would like to protect all pages, you can create a `_middleware.js` file in your root `pages` directory which looks like this:
```js title="/middleware.js"
export { default } from "next-auth/middleware"
```
Otherwise, if you only want to protect a subset of pages, you could put it in a subdirectory as well, for example in `/pages/admin/_middleware.js` would protect all pages under `/admin`.
If you only want to secure certain pages, export a `config` object with a `matcher`:
```js
export { default } from "next-auth/middleware"
export const config = { matcher: ["/dashboard"] }
```
For the time being, the `withAuth` middleware only supports `"jwt"` as [session strategy](https://next-auth.js.org/configuration/options#session).

View File

@@ -7,7 +7,7 @@
"build:app": "turbo run build --filter=next-auth-app --include-dependencies",
"build": "turbo run build --filter=next-auth --filter=@next-auth/* --no-deps",
"lint": "turbo run lint --filter=!next-auth-docs --parallel",
"test": "turbo run test --concurrency=1 --filter=!@next-auth/pouchdb-adapter --filter=!@next-auth/mikro-orm-adapter --filter=!@next-auth/upstash-redis-adapter --filter=!next-auth-* --filter=[HEAD^1]",
"test": "turbo run test --concurrency=1 --filter=!@next-auth/pouchdb-adapter --filter=!@next-auth/upstash-redis-adapter --filter=!next-auth-* --filter=[HEAD^1]",
"clean": "turbo run clean --no-cache",
"dev:app": "turbo run dev --parallel --continue --filter=next-auth-app...",
"dev:docs": "turbo run dev --filter=next-auth-docs",

View File

@@ -1,6 +1,6 @@
{
"name": "@next-auth/mikro-orm-adapter",
"version": "2.0.1",
"version": "3.0.0",
"description": "MikroORM adapter for next-auth.",
"homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/next-auth",
@@ -32,22 +32,22 @@
"dist"
],
"peerDependencies": {
"@mikro-orm/core": "^5.0.2",
"@mikro-orm/core": "^5",
"next-auth": "^4"
},
"devDependencies": {
"@mikro-orm/core": "^5.0.2",
"@mikro-orm/sqlite": "^5.0.2",
"@mikro-orm/core": "^5",
"@mikro-orm/sqlite": "^5",
"@next-auth/adapter-test": "workspace:*",
"@next-auth/tsconfig": "workspace:*",
"@types/uuid": "^8.3.3",
"jest": "^27.4.3",
"@types/uuid": ">=8",
"jest": "^29",
"next-auth": "workspace:*"
},
"dependencies": {
"uuid": "^9"
},
"jest": {
"preset": "@next-auth/adapter-test/jest"
},
"dependencies": {
"uuid": "^8.3.2"
}
}
}

View File

@@ -9,6 +9,7 @@ import {
OneToMany,
Collection,
ManyToOne,
types,
} from "@mikro-orm/core"
import type { DefaultAccount } from "next-auth"
@@ -29,55 +30,56 @@ export class User implements RemoveIndex<AdapterUser> {
@PrimaryKey()
id: string = randomUUID()
@Property({ nullable: true })
@Property({ type: types.string, nullable: true })
name?: string
@Property({ nullable: true })
@Property({ type: types.string, nullable: true })
@Unique()
email?: string
@Property({ type: "Date", nullable: true })
@Property({ type: types.datetime, nullable: true })
emailVerified: Date | null = null
@Property({ nullable: true })
@Property({ type: types.string, nullable: true })
image?: string
@OneToMany({
entity: () => Session,
mappedBy: (session) => session.user,
entity: 'Session',
mappedBy: (session: Session) => session.user,
hidden: true,
orphanRemoval: true,
})
sessions = new Collection<Session>(this)
sessions = new Collection<Session, object>(this)
@OneToMany({
entity: () => Account,
mappedBy: (account) => account.user,
entity: 'Account',
mappedBy: (account: Account) => account.user,
hidden: true,
orphanRemoval: true,
})
accounts = new Collection<Account>(this)
accounts = new Collection<Account, object>(this)
}
@Entity()
export class Session implements AdapterSession {
@PrimaryKey()
@Property({ type: types.string })
id: string = randomUUID()
@ManyToOne({
entity: () => User,
entity: 'User',
hidden: true,
onDelete: "cascade",
})
user!: User
@Property({ persist: false })
@Property({ type: types.string, persist: false })
userId!: string
@Property()
@Property({ type: 'Date' })
expires!: Date
@Property()
@Property({ type: types.string })
@Unique()
sessionToken!: string
}
@@ -86,46 +88,47 @@ export class Session implements AdapterSession {
@Unique({ properties: ["provider", "providerAccountId"] })
export class Account implements RemoveIndex<DefaultAccount> {
@PrimaryKey()
@Property({ type: types.string })
id: string = randomUUID()
@ManyToOne({
entity: () => User,
entity: 'User',
hidden: true,
onDelete: "cascade",
})
user!: User
@Property({ persist: false })
@Property({ type: types.string, persist: false })
userId!: string
@Enum()
@Property({ type: types.string })
type!: ProviderType
@Property()
@Property({ type: types.string })
provider!: string
@Property()
@Property({ type: types.string })
providerAccountId!: string
@Property({ nullable: true })
@Property({ type: types.string, nullable: true })
refresh_token?: string
@Property({ nullable: true })
@Property({ type: types.string, nullable: true })
access_token?: string
@Property({ nullable: true })
@Property({ type: types.integer, nullable: true })
expires_at?: number
@Property({ nullable: true })
@Property({ type: types.string, nullable: true })
token_type?: string
@Property({ nullable: true })
@Property({ type: types.string, nullable: true })
scope?: string
@Property({ nullable: true })
@Property({ type: types.text, nullable: true })
id_token?: string
@Property({ nullable: true })
@Property({ type: types.string, nullable: true })
session_state?: string
}
@@ -133,12 +136,12 @@ export class Account implements RemoveIndex<DefaultAccount> {
@Unique({ properties: ["token", "identifier"] })
export class VerificationToken implements AdapterVerificationToken {
@PrimaryKey()
@Property()
@Property({ type: types.string })
token!: string
@Property()
@Property({ type: 'Date' })
expires!: Date
@Property()
@Property({ type: types.string })
identifier!: string
}

View File

@@ -10,7 +10,7 @@ import { MikroORM, wrap } from "@mikro-orm/core"
import * as defaultEntities from "./entities"
export * as defaultEntities from "./entities"
export { defaultEntities }
/**
* The MikroORM adapter accepts a MikroORM configuration and returns a NextAuth adapter.

View File

@@ -0,0 +1,591 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`run migrations: createSchemaSQL 1`] = `
"pragma foreign_keys = off;
create table \`user\` (\`id\` text not null, \`name\` text null, \`email\` text null, \`email_verified\` datetime null, \`image\` text null, primary key (\`id\`));
create unique index \`user_email_unique\` on \`user\` (\`email\`);
create table \`session\` (\`id\` text not null, \`user_id\` text not null, \`expires\` datetime not null, \`session_token\` text not null, constraint \`session_user_id_foreign\` foreign key(\`user_id\`) references \`user\`(\`id\`) on delete cascade on update cascade, primary key (\`id\`));
create index \`session_user_id_index\` on \`session\` (\`user_id\`);
create unique index \`session_session_token_unique\` on \`session\` (\`session_token\`);
create table \`account\` (\`id\` text not null, \`user_id\` text not null, \`type\` text not null, \`provider\` text not null, \`provider_account_id\` text not null, \`refresh_token\` text null, \`access_token\` text null, \`expires_at\` integer null, \`token_type\` text null, \`scope\` text null, \`id_token\` text null, \`session_state\` text null, constraint \`account_user_id_foreign\` foreign key(\`user_id\`) references \`user\`(\`id\`) on delete cascade on update cascade, primary key (\`id\`));
create index \`account_user_id_index\` on \`account\` (\`user_id\`);
create unique index \`account_provider_provider_account_id_unique\` on \`account\` (\`provider\`, \`provider_account_id\`);
create table \`verification_token\` (\`token\` text not null, \`expires\` datetime not null, \`identifier\` text not null, primary key (\`token\`));
create unique index \`verification_token_token_identifier_unique\` on \`verification_token\` (\`token\`, \`identifier\`);
pragma foreign_keys = on;
"
`;
exports[`run migrations: targetSchema 1`] = `
{
"name": undefined,
"namespaces": [],
"tables": [
{
"checks": [],
"columns": {
"email": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "email",
"nullable": true,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"email_verified": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": 0,
"mappedType": "datetime",
"name": "email_verified",
"nullable": true,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "datetime",
"unsigned": false,
},
"id": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "id",
"nullable": false,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"image": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "image",
"nullable": true,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"name": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "name",
"nullable": true,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
},
"comment": undefined,
"foreignKeys": {},
"indexes": [
{
"columnNames": [
"email",
],
"composite": false,
"keyName": "user_email_unique",
"primary": false,
"unique": true,
},
{
"columnNames": [
"id",
],
"composite": false,
"expression": undefined,
"keyName": "primary",
"primary": true,
"type": undefined,
"unique": true,
},
],
"name": "user",
"schema": undefined,
},
{
"checks": [],
"columns": {
"expires": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": 0,
"mappedType": "datetime",
"name": "expires",
"nullable": false,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "datetime",
"unsigned": false,
},
"id": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "id",
"nullable": false,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"session_token": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "session_token",
"nullable": false,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"user_id": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "user_id",
"nullable": false,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
},
"comment": undefined,
"foreignKeys": {
"session_user_id_foreign": {
"columnNames": [
"user_id",
],
"constraintName": "session_user_id_foreign",
"deleteRule": "cascade",
"localTableName": "session",
"referencedColumnNames": [
"id",
],
"referencedTableName": "user",
"updateRule": "cascade",
},
},
"indexes": [
{
"columnNames": [
"user_id",
],
"composite": false,
"keyName": "session_user_id_index",
"primary": false,
"unique": false,
},
{
"columnNames": [
"session_token",
],
"composite": false,
"keyName": "session_session_token_unique",
"primary": false,
"unique": true,
},
{
"columnNames": [
"id",
],
"composite": false,
"expression": undefined,
"keyName": "primary",
"primary": true,
"type": undefined,
"unique": true,
},
],
"name": "session",
"schema": undefined,
},
{
"checks": [],
"columns": {
"access_token": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "access_token",
"nullable": true,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"expires_at": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "integer",
"name": "expires_at",
"nullable": true,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "integer",
"unsigned": false,
},
"id": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "id",
"nullable": false,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"id_token": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "id_token",
"nullable": true,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"provider": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "provider",
"nullable": false,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"provider_account_id": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "provider_account_id",
"nullable": false,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"refresh_token": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "refresh_token",
"nullable": true,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"scope": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "scope",
"nullable": true,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"session_state": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "session_state",
"nullable": true,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"token_type": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "token_type",
"nullable": true,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"type": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "type",
"nullable": false,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"user_id": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "user_id",
"nullable": false,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
},
"comment": undefined,
"foreignKeys": {
"account_user_id_foreign": {
"columnNames": [
"user_id",
],
"constraintName": "account_user_id_foreign",
"deleteRule": "cascade",
"localTableName": "account",
"referencedColumnNames": [
"id",
],
"referencedTableName": "user",
"updateRule": "cascade",
},
},
"indexes": [
{
"columnNames": [
"user_id",
],
"composite": false,
"keyName": "account_user_id_index",
"primary": false,
"unique": false,
},
{
"columnNames": [
"provider",
"provider_account_id",
],
"composite": true,
"expression": undefined,
"keyName": "account_provider_provider_account_id_unique",
"primary": false,
"type": undefined,
"unique": true,
},
{
"columnNames": [
"id",
],
"composite": false,
"expression": undefined,
"keyName": "primary",
"primary": true,
"type": undefined,
"unique": true,
},
],
"name": "account",
"schema": undefined,
},
{
"checks": [],
"columns": {
"expires": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": 0,
"mappedType": "datetime",
"name": "expires",
"nullable": false,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "datetime",
"unsigned": false,
},
"identifier": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "identifier",
"nullable": false,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
"token": {
"autoincrement": false,
"comment": undefined,
"default": undefined,
"enumItems": undefined,
"extra": undefined,
"length": undefined,
"mappedType": "text",
"name": "token",
"nullable": false,
"precision": undefined,
"primary": false,
"scale": undefined,
"type": "text",
"unsigned": false,
},
},
"comment": undefined,
"foreignKeys": {},
"indexes": [
{
"columnNames": [
"token",
"identifier",
],
"composite": true,
"expression": undefined,
"keyName": "verification_token_token_identifier_unique",
"primary": false,
"type": undefined,
"unique": true,
},
{
"columnNames": [
"token",
],
"composite": false,
"expression": undefined,
"keyName": "primary",
"primary": true,
"type": undefined,
"unique": true,
},
],
"name": "verification_token",
"schema": undefined,
},
],
}
`;

View File

@@ -1,10 +1,69 @@
import type { Options } from "@mikro-orm/core"
import { Options, types } from "@mikro-orm/core"
import type { SqliteDriver } from "@mikro-orm/sqlite"
import { MikroORM, wrap } from "@mikro-orm/core"
import { runBasicTests } from "@next-auth/adapter-test"
import { MikroOrmAdapter, defaultEntities } from "../src"
import { User, VeryImportantEntity } from "./testEntities"
import {
Cascade,
Collection,
Entity,
OneToMany,
PrimaryKey,
Property,
Unique,
} from "@mikro-orm/core"
import { randomUUID } from "@next-auth/adapter-test"
@Entity()
export class User implements defaultEntities.User {
@PrimaryKey()
@Property({ type: types.string })
id: string = randomUUID()
@Property({ type: types.string, nullable: true })
name?: string
@Property({ type: types.string, nullable: true })
@Unique()
email?: string
@Property({ type: 'Date', nullable: true })
emailVerified: Date | null = null
@Property({ type: types.string, nullable: true })
image?: string
@OneToMany({
entity: 'Session',
mappedBy: (session: defaultEntities.Session) => session.user,
hidden: true,
orphanRemoval: true,
cascade: [Cascade.ALL],
})
sessions = new Collection<defaultEntities.Session>(this)
@OneToMany({
entity: 'Account',
mappedBy: (account: defaultEntities.Account) => account.user,
hidden: true,
orphanRemoval: true,
cascade: [Cascade.ALL],
})
accounts = new Collection<defaultEntities.Account>(this)
@Property({ type: types.string, hidden: true })
role = "ADMIN"
}
@Entity()
export class VeryImportantEntity {
@PrimaryKey()
@Property({ type: types.string })
id: string = randomUUID()
@Property({ type: types.boolean })
important = true
}
let _init: MikroORM

View File

@@ -0,0 +1,28 @@
import { MikroORM, Options } from "@mikro-orm/core";
import { SqliteDriver } from "@mikro-orm/sqlite";
import { defaultEntities } from "../src";
const config: Options<SqliteDriver> = {
dbName: "./db.sqlite",
type: "sqlite",
entities: [
defaultEntities.User,
defaultEntities.Account,
defaultEntities.Session,
defaultEntities.VerificationToken,
],
}
it("run migrations", async () => {
const orm = await MikroORM.init(config)
await orm.getSchemaGenerator().dropSchema()
const createSchemaSQL = await orm.getSchemaGenerator().getCreateSchemaSQL()
expect(createSchemaSQL).toMatchSnapshot('createSchemaSQL')
const targetSchema = await orm.getSchemaGenerator().getTargetSchema()
expect(targetSchema).toMatchSnapshot('targetSchema')
await orm.getSchemaGenerator().dropSchema()
await orm.close().catch(() => null)
})

View File

@@ -1,61 +0,0 @@
import {
Cascade,
Collection,
Entity,
OneToMany,
PrimaryKey,
Property,
Unique,
} from "@mikro-orm/core"
import { randomUUID } from "@next-auth/adapter-test"
import type { defaultEntities } from "../src"
import { Account, Session } from "../src/entities"
@Entity()
export class User implements defaultEntities.User {
@PrimaryKey()
id: string = randomUUID()
@Property({ nullable: true })
name?: string
@Property({ nullable: true })
@Unique()
email?: string
@Property({ type: "Date", nullable: true })
emailVerified: Date | null = null
@Property({ nullable: true })
image?: string
@OneToMany({
entity: () => Session,
mappedBy: (session) => session.user,
hidden: true,
orphanRemoval: true,
cascade: [Cascade.ALL],
})
sessions = new Collection<Session>(this)
@OneToMany({
entity: () => Account,
mappedBy: (account) => account.user,
hidden: true,
orphanRemoval: true,
cascade: [Cascade.ALL],
})
accounts = new Collection<Account>(this)
@Property({ hidden: true })
role = "ADMIN"
}
@Entity()
export class VeryImportantEntity {
@PrimaryKey()
id: string = randomUUID()
@Property()
important = true
}

View File

@@ -5,4 +5,4 @@
"./*.js",
"./*.d.ts",
]
}
}

View File

@@ -8,5 +8,6 @@
"outDir": "dist",
"stripInternal": true
},
"exclude": ["tests", "dist", "jest.config.js"]
"include": ["src"],
"exclude": ["dist", "test", "node_modules"]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@next-auth/mongodb-adapter",
"version": "1.0.4",
"version": "1.1.0",
"description": "mongoDB adapter for next-auth.",
"homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/next-auth",

View File

@@ -1,6 +1,6 @@
{
"name": "@next-auth/upstash-redis-adapter",
"version": "3.0.1",
"version": "3.0.2",
"description": "Upstash adapter for next-auth. It uses Upstash's connectionless (HTTP based) Redis client.",
"homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/next-auth",

View File

@@ -165,13 +165,20 @@ export function UpstashRedisAdapter(
},
async createVerificationToken(verificationToken) {
await setObjectAsJson(
verificationTokenKeyPrefix + verificationToken.identifier,
verificationTokenKeyPrefix +
verificationToken.identifier +
":" +
verificationToken.token,
verificationToken
)
return verificationToken
},
async useVerificationToken(verificationToken) {
const tokenKey = verificationTokenKeyPrefix + verificationToken.identifier
const tokenKey =
verificationTokenKeyPrefix +
verificationToken.identifier +
":" +
verificationToken.token
const token = await client.get<VerificationToken>(tokenKey)
if (!token) return null

View File

@@ -1,6 +1,6 @@
{
"name": "next-auth",
"version": "4.10.3",
"version": "4.12.0",
"description": "Authentication for Next.js",
"homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/next-auth.git",
@@ -69,8 +69,8 @@
"dependencies": {
"@babel/runtime": "^7.16.3",
"@panva/hkdf": "^1.0.1",
"cookie": "^0.4.1",
"jose": "^4.3.7",
"cookie": "^0.5.0",
"jose": "^4.9.3",
"oauth": "^0.9.15",
"openid-client": "^5.1.0",
"preact": "^10.6.3",
@@ -78,6 +78,7 @@
"uuid": "^8.3.2"
},
"peerDependencies": {
"next": "12.2.5",
"nodemailer": "^6.6.5",
"react": "^17.0.2 || ^18",
"react-dom": "^17.0.2 || ^18"
@@ -118,7 +119,7 @@
"jest-environment-jsdom": "^28.1.1",
"jest-watch-typeahead": "^1.1.0",
"msw": "^0.42.3",
"next": "12.2.0",
"next": "12.2.5",
"postcss": "^8.4.14",
"postcss-cli": "^9.1.0",
"postcss-nested": "^5.0.6",

View File

@@ -1,3 +1,4 @@
import { randomBytes, randomUUID } from "crypto"
import { NextAuthOptions } from ".."
import logger from "../utils/logger"
import parseUrl from "../utils/parse-url"
@@ -86,6 +87,10 @@ export async function init({
strategy: userOptions.adapter ? "database" : "jwt",
maxAge,
updateAge: 24 * 60 * 60,
generateSessionToken: () => {
// Use `randomUUID` if available. (Node 15.6+)
return randomUUID?.() ?? randomBytes(32).toString("hex")
},
...userOptions.session,
},
// JWT options

View File

@@ -1,4 +1,3 @@
import { randomBytes, randomUUID } from "crypto"
import { AccountNotLinkedError } from "../errors"
import { fromDate } from "./utils"
@@ -37,7 +36,7 @@ export default async function callbackHandler(params: {
adapter,
jwt,
events,
session: { strategy: sessionStrategy },
session: { strategy: sessionStrategy, generateSessionToken },
} = options
// If no adapter is configured then we don't have a database and cannot
@@ -219,8 +218,3 @@ export default async function callbackHandler(params: {
}
}
}
function generateSessionToken() {
// Use `randomUUID` if available. (Node 15.6++)
return randomUUID?.() ?? randomBytes(32).toString("hex")
}

View File

@@ -468,6 +468,13 @@ export interface SessionOptions {
* @default 86400 // 1 day
*/
updateAge: number
/**
* Generate a custom session token for database-based sessions.
* By default, a random UUID or string is generated depending on the Node.js version.
* However, you can specify your own custom string (such as CUID) to be used.
* @default `randomUUID` or `randomBytes.toHex` depending on the Node.js version
*/
generateSessionToken: () => string
}
export interface DefaultUser {

View File

@@ -106,12 +106,13 @@ async function handleMiddleware(
const signInPage = options?.pages?.signIn ?? "/api/auth/signin"
const errorPage = options?.pages?.error ?? "/api/auth/error"
const basePath = parseUrl(process.env.NEXTAUTH_URL).path
const publicPaths = [signInPage, errorPage, "/_next", "/favicon.ico"]
const publicPaths = ["/_next", "/favicon.ico"]
// Avoid infinite redirects/invalid response
// on paths that never require authentication
if (
pathname.startsWith(basePath) ||
[signInPage, errorPage].includes(pathname) ||
publicPaths.some((p) => pathname.startsWith(p))
) {
return

View File

@@ -0,0 +1,40 @@
import { NextMiddleware } from "next/server"
import { NextAuthMiddlewareOptions, withAuth } from "../next/middleware"
it("should not match pages as public paths", async () => {
const options: NextAuthMiddlewareOptions = {
pages: {
signIn: "/",
error: "/"
},
secret: "secret"
}
const nextUrl: any = {
pathname: "/protected/pathA",
search: "",
origin: "http://127.0.0.1"
}
const req: any = { nextUrl, headers: { authorization: "" } }
const handleMiddleware = withAuth(options) as NextMiddleware
const res = await handleMiddleware(req, null)
expect(res).toBeDefined()
expect(res.status).toBe(307)
})
it("should not redirect on public paths", async () => {
const options: NextAuthMiddlewareOptions = {
secret: "secret"
}
const nextUrl: any = {
pathname: "/_next/foo",
search: "",
origin: "http://127.0.0.1"
}
const req: any = { nextUrl, headers: { authorization: "" } }
const handleMiddleware = withAuth(options) as NextMiddleware
const res = await handleMiddleware(req, null)
expect(res).toBeUndefined()
})

1246
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff