mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
21 Commits
v3.15.8
...
v4.0.0-nex
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edcb10a823 | ||
|
|
2acabe19e0 | ||
|
|
ad4709764a | ||
|
|
55a2932973 | ||
|
|
49cb7e5bd7 | ||
|
|
b95182ded7 | ||
|
|
be28672fd4 | ||
|
|
e26c5fc905 | ||
|
|
543f812eb3 | ||
|
|
0c9f9777c5 | ||
|
|
34f334a71d | ||
|
|
a6f5f4c184 | ||
|
|
9fa93e3b5e | ||
|
|
cb4342fdda | ||
|
|
5f717b3914 | ||
|
|
d09a45ec7c | ||
|
|
930f58eba3 | ||
|
|
c20b7f2930 | ||
|
|
e418cddd96 | ||
|
|
111e7aabdf | ||
|
|
a113ef6fab |
27
.gitignore
vendored
27
.gitignore
vendored
@@ -25,20 +25,21 @@ node_modules
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
.next
|
||||
www/providers.json
|
||||
src/providers/index.js
|
||||
internals
|
||||
adapters.d.ts
|
||||
adapters.js
|
||||
client.d.ts
|
||||
client.js
|
||||
index.d.ts
|
||||
index.js
|
||||
jwt.d.ts
|
||||
jwt.js
|
||||
providers.d.ts
|
||||
providers.js
|
||||
/internals
|
||||
/adapters.d.ts
|
||||
/adapters.js
|
||||
/client.d.ts
|
||||
/client.js
|
||||
/index.d.ts
|
||||
/index.js
|
||||
/jwt.d.ts
|
||||
/jwt.js
|
||||
/providers.d.ts
|
||||
/providers.js
|
||||
/errors.js
|
||||
/errors.d.ts
|
||||
|
||||
# Development app
|
||||
app/next-auth
|
||||
@@ -57,4 +58,4 @@ app/yarn.lock
|
||||
/_work
|
||||
|
||||
# Prisma migrations
|
||||
/prisma/migrations
|
||||
/prisma/migrations
|
||||
|
||||
@@ -82,6 +82,6 @@ export default NextAuth({
|
||||
// Prisma Database Adapter
|
||||
// To configure this app to use the schema in `prisma/schema.prisma` run:
|
||||
// npx prisma generate
|
||||
// npx prisma migrate dev --preview-feature
|
||||
// npx prisma migrate dev
|
||||
// adapter: Adapters.Prisma.Adapter({ prisma })
|
||||
})
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
{
|
||||
"presets": [
|
||||
["@babel/preset-env", { "targets": { "esmodules": true } }]
|
||||
["@babel/preset-env", { "targets": { "node": "10" } }]
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties"
|
||||
],
|
||||
"comments": false,
|
||||
"overrides": [
|
||||
{
|
||||
"test": ["../src/client/**"],
|
||||
"comments": true,
|
||||
"presets": [
|
||||
["@babel/preset-env", { "targets": { "ie": "11" } }]
|
||||
]
|
||||
},
|
||||
{
|
||||
"test": ["../src/server/pages/**"],
|
||||
"presets": ["preact"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ const MODULE_ENTRIES = {
|
||||
PROVIDERS: "providers",
|
||||
ADAPTERS: "adapters",
|
||||
JWT: "jwt",
|
||||
ERRORS: "errors",
|
||||
}
|
||||
|
||||
// Building submodule entries
|
||||
@@ -17,6 +18,7 @@ const BUILD_TARGETS = {
|
||||
[`${MODULE_ENTRIES.ADAPTERS}.js`]: "module.exports = require('./dist/adapters').default\n",
|
||||
[`${MODULE_ENTRIES.PROVIDERS}.js`]: "module.exports = require('./dist/providers').default\n",
|
||||
[`${MODULE_ENTRIES.JWT}.js`]: "module.exports = require('./dist/lib/jwt').default\n",
|
||||
[`${MODULE_ENTRIES.ERRORS}.js`]: "module.exports = require('./dist/lib/errors').default\n",
|
||||
}
|
||||
|
||||
Object.entries(BUILD_TARGETS).forEach(([target, content]) => {
|
||||
@@ -34,6 +36,7 @@ const TYPES_TARGETS = [
|
||||
`${MODULE_ENTRIES.ADAPTERS}.d.ts`,
|
||||
`${MODULE_ENTRIES.PROVIDERS}.d.ts`,
|
||||
`${MODULE_ENTRIES.JWT}.d.ts`,
|
||||
`${MODULE_ENTRIES.ERRORS}.d.ts`,
|
||||
"internals",
|
||||
]
|
||||
|
||||
|
||||
2264
package-lock.json
generated
2264
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
72
package.json
72
package.json
@@ -7,14 +7,26 @@
|
||||
"author": "Iain Collins <me@iaincollins.com>",
|
||||
"main": "index.js",
|
||||
"types": "./index.d.ts",
|
||||
"keywords": ["react", "nodejs", "oauth", "jwt", "oauth2", "authentication", "nextjs", "csrf", "oidc", "nextauth"],
|
||||
"keywords": [
|
||||
"react",
|
||||
"nodejs",
|
||||
"oauth",
|
||||
"jwt",
|
||||
"oauth2",
|
||||
"authentication",
|
||||
"nextjs",
|
||||
"csrf",
|
||||
"oidc",
|
||||
"nextauth"
|
||||
],
|
||||
"exports": {
|
||||
".": "./dist/server/index.js",
|
||||
"./jwt": "./dist/lib/jwt.js",
|
||||
"./adapters": "./dist/adapters/index.js",
|
||||
"./client": "./dist/client/index.js",
|
||||
"./providers": "./dist/providers/index.js",
|
||||
"./providers/*": "./dist/providers/*.js"
|
||||
"./providers/*": "./dist/providers/*.js",
|
||||
"./errors": "./dist/lib/errors.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run build:js && npm run build:css",
|
||||
@@ -54,39 +66,61 @@
|
||||
"adapters.d.ts",
|
||||
"client.js",
|
||||
"client.d.ts",
|
||||
"errors.js",
|
||||
"errors.d.ts",
|
||||
"jwt.js",
|
||||
"jwt.d.ts",
|
||||
"internals"
|
||||
],
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"crypto-js": "^4.0.0",
|
||||
"@balazsorban/require-optional": "^1.0.0",
|
||||
"futoin-hkdf": "^1.3.2",
|
||||
"jose": "^1.27.2",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"nodemailer": "^6.4.16",
|
||||
"oauth": "^0.9.15",
|
||||
"pkce-challenge": "^2.1.0",
|
||||
"preact": "^10.4.1",
|
||||
"preact-render-to-string": "^5.1.14",
|
||||
"querystring": "^0.2.0",
|
||||
"require_optional": "^1.0.1",
|
||||
"typeorm": "^0.2.30"
|
||||
"preact-render-to-string": "^5.1.14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17",
|
||||
"react-dom": "16.13.1 || ^17"
|
||||
},
|
||||
"peerOptionalDependencies": {
|
||||
"mongodb": "^3.5.9",
|
||||
"react-dom": "^16.13.1 || ^17",
|
||||
"mongodb": "^3.6.6",
|
||||
"mysql": "^2.18.1",
|
||||
"mssql": "^6.2.1",
|
||||
"pg": "^8.2.1",
|
||||
"@prisma/client": "^2.16.1"
|
||||
"@prisma/client": "^2.16.1",
|
||||
"nodemailer": "^6.4.16",
|
||||
"typeorm": "^0.2.30"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"mongodb": {
|
||||
"optional": true
|
||||
},
|
||||
"mysql": {
|
||||
"optional": true
|
||||
},
|
||||
"mssql": {
|
||||
"optional": true
|
||||
},
|
||||
"pg": {
|
||||
"optional": true
|
||||
},
|
||||
"@prisma/client": {
|
||||
"optional": true
|
||||
},
|
||||
"nodemailer": {
|
||||
"optional": true
|
||||
},
|
||||
"typeorm": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.8.4",
|
||||
"@babel/core": "^7.9.6",
|
||||
"@babel/plugin-proposal-class-properties": "^7.13.0",
|
||||
"@babel/preset-env": "^7.9.6",
|
||||
"@prisma/client": "^2.16.1",
|
||||
"@semantic-release/commit-analyzer": "^8.0.1",
|
||||
@@ -110,7 +144,7 @@
|
||||
"eslint-plugin-promise": "^4.3.1",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"mocha": "^8.1.3",
|
||||
"mongodb": "^3.5.9",
|
||||
"mongodb": "^3.6.6",
|
||||
"mssql": "^6.2.1",
|
||||
"mysql": "^2.18.1",
|
||||
"next": "^10.0.5",
|
||||
@@ -157,8 +191,14 @@
|
||||
"branches": [
|
||||
"+([0-9])?(.{+([0-9]),x}).x",
|
||||
"main",
|
||||
{ "name": "beta", "prerelease": true },
|
||||
{ "name": "next", "prerelease": true }
|
||||
{
|
||||
"name": "beta",
|
||||
"prerelease": true
|
||||
},
|
||||
{
|
||||
"name": "next",
|
||||
"prerelease": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"funding": [
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createConnection, getConnection } from 'typeorm'
|
||||
import { createHash } from 'crypto'
|
||||
import require_optional from 'require_optional' // eslint-disable-line camelcase
|
||||
|
||||
import { CreateUserError } from '../../lib/errors'
|
||||
import adapterConfig from './lib/config'
|
||||
@@ -9,6 +8,8 @@ import Models from './models'
|
||||
|
||||
import { updateConnectionEntities } from './lib/utils'
|
||||
|
||||
import requireOptional from '@balazsorban/require-optional'
|
||||
|
||||
const Adapter = (typeOrmConfig, options = {}) => {
|
||||
// Ensure typeOrmConfigObject is normalized to an object
|
||||
const typeOrmConfigObject = (typeof typeOrmConfig === 'string')
|
||||
@@ -94,12 +95,12 @@ const Adapter = (typeOrmConfig, options = {}) => {
|
||||
let ObjectId
|
||||
if (config.type === 'mongodb') {
|
||||
idKey = '_id'
|
||||
// Using a dynamic import causes problems for some compilers/bundlers
|
||||
// that don't handle dynamic imports. To try and work around this we are
|
||||
// using the same method mongodb uses to load Object ID type, which is to
|
||||
// use the require_optional loader.
|
||||
const mongodb = require_optional('mongodb')
|
||||
ObjectId = mongodb.ObjectId
|
||||
// We should/could use dynamic import here, but
|
||||
// bundlers like webpack will try to import the module,
|
||||
// even if this conditional branch is never entered.
|
||||
// We work around this with requireOptional.
|
||||
const mongodb = requireOptional('mongodb')
|
||||
ObjectId = mongodb.ObjectID
|
||||
}
|
||||
|
||||
// These values are stored as seconds, but to use them with dates in
|
||||
|
||||
103
src/client/index.d.ts
vendored
Normal file
103
src/client/index.d.ts
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
import * as React from 'react'
|
||||
import { GetServerSidePropsContext } from 'next'
|
||||
|
||||
interface DefaultSession {
|
||||
user: {
|
||||
name: string | null
|
||||
email: string | null
|
||||
image: string | null
|
||||
}
|
||||
expires: Date | string
|
||||
}
|
||||
|
||||
interface BroadcastMessage {
|
||||
event?: 'session'
|
||||
data?: {
|
||||
trigger?: 'signout' | 'getSession'
|
||||
}
|
||||
clientId: string
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
type GetSession<S extends Record<string, unknown> = DefaultSession> = (options: {
|
||||
ctx?: GetServerSidePropsContext
|
||||
req?: GetServerSidePropsContext['req']
|
||||
event?: 'storage' | 'timer' | 'hidden' | string
|
||||
triggerEvent?: boolean
|
||||
}) => Promise<S>
|
||||
|
||||
export interface NextAuthConfig {
|
||||
baseUrl: string
|
||||
basePath: string
|
||||
baseUrlServer: string
|
||||
basePathServer: string
|
||||
/** 0 means disabled (don't send); 60 means send every 60 seconds */
|
||||
keepAlive: number
|
||||
/** 0 means disabled (only use cache); 60 means sync if last checked > 60 seconds ago */
|
||||
clientMaxAge: number
|
||||
/** Used for timestamp since last sycned (in seconds) */
|
||||
_clientLastSync: number
|
||||
/** Stores timer for poll interval */
|
||||
_clientSyncTimer: ReturnType<typeof setTimeout>
|
||||
/** Tracks if event listeners have been added */
|
||||
_eventListenersAdded: boolean
|
||||
/** Stores last session response from hook */
|
||||
_clientSession: DefaultSession | null | undefined
|
||||
/** Used to store to function export by getSession() hook */
|
||||
_getSession: any
|
||||
}
|
||||
|
||||
export type GetCsrfToken = (
|
||||
ctxOrReq: GetServerSidePropsContext & GetServerSidePropsContext['req']
|
||||
) => Promise<string | null>
|
||||
|
||||
export interface SessionOptions {
|
||||
baseUrl?: string
|
||||
basePath?: string
|
||||
clientMaxAge?: number
|
||||
keepAlive?: number
|
||||
}
|
||||
|
||||
export type Provider<S extends Record<string, unknown> = DefaultSession > = (options: {
|
||||
children: React.ReactNode
|
||||
session: S
|
||||
options: SessionOptions
|
||||
}) => React.ReactNode
|
||||
|
||||
export type SetOptions = (options: SessionOptions) => void
|
||||
|
||||
export type SessionContext = React.createContext<[DefaultSession | null, boolean]>
|
||||
|
||||
export type UseSession = () => [any, boolean]
|
||||
|
||||
export type GetProviders = () => Promise<any[]>
|
||||
|
||||
// Sign in types
|
||||
|
||||
export interface SignInOptions {
|
||||
/** Defaults to the current URL. */
|
||||
callbackUrl?: string
|
||||
redirect?: boolean
|
||||
}
|
||||
export interface SignInResponse {
|
||||
error: string | null
|
||||
status: number
|
||||
ok: boolean
|
||||
url: string | null
|
||||
}
|
||||
|
||||
export type SignIn<AuthorizationParams = Record<string, string>> = (
|
||||
provider?: string,
|
||||
options?: SignInOptions,
|
||||
authorizationParams?: AuthorizationParams
|
||||
) => SignInResponse
|
||||
|
||||
// Sign out types
|
||||
|
||||
interface SignOutResponse<RedirectType extends boolean=true> {
|
||||
/** Defaults to the current URL. */
|
||||
callbackUrl?: string
|
||||
redirect?: RedirectType
|
||||
}
|
||||
|
||||
export type SignOut<RedirectType extends boolean = true> = (params: SignOutResponse<RedirectType>) => RedirectType extends true ? Promise<{url?: string} | undefined> : undefined
|
||||
@@ -1,39 +1,98 @@
|
||||
/**
|
||||
* Same as the default `Error`, but it is JSON serializable.
|
||||
* @source https://iaincollins.medium.com/error-handling-in-javascript-a6172ccdf9af
|
||||
*/
|
||||
export class UnknownError extends Error {
|
||||
constructor (message) {
|
||||
super(message)
|
||||
this.name = 'UnknownError'
|
||||
constructor(error) {
|
||||
// Support passing error or string
|
||||
super(error?.message ?? error)
|
||||
this.name = "UnknownError"
|
||||
if (error instanceof Error) {
|
||||
this.stack = error.stack
|
||||
}
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
toJSON() {
|
||||
return {
|
||||
error: {
|
||||
name: this.name,
|
||||
message: this.message
|
||||
// stack: this.stack
|
||||
}
|
||||
name: this.name,
|
||||
message: this.message,
|
||||
stack: this.stack,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CreateUserError extends UnknownError {
|
||||
constructor (message) {
|
||||
super(message)
|
||||
this.name = 'CreateUserError'
|
||||
}
|
||||
}
|
||||
|
||||
// Thrown when an Email address is already associated with an account
|
||||
// but the user is trying an OAuth account that is not linked to it.
|
||||
export class AccountNotLinkedError extends UnknownError {
|
||||
constructor (message) {
|
||||
super(message)
|
||||
this.name = 'AccountNotLinkedError'
|
||||
}
|
||||
}
|
||||
|
||||
export class OAuthCallbackError extends UnknownError {
|
||||
constructor (message) {
|
||||
super(message)
|
||||
this.name = 'OAuthCallbackError'
|
||||
}
|
||||
name = "OAuthCallbackError"
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when an Email address is already associated with an account
|
||||
* but the user is trying an OAuth account that is not linked to it.
|
||||
*/
|
||||
export class AccountNotLinkedError extends UnknownError {
|
||||
name = "AccountNotLinkedError"
|
||||
}
|
||||
|
||||
export class CreateUserError extends UnknownError {
|
||||
name = "CreateUserError"
|
||||
}
|
||||
|
||||
export class GetUserError extends UnknownError {
|
||||
name = "GetUserError"
|
||||
}
|
||||
|
||||
export class GetUserByEmailError extends UnknownError {
|
||||
name = "GetUserByEmailError"
|
||||
}
|
||||
|
||||
export class GetUserByIdError extends UnknownError {
|
||||
name = "GetUserByIdError"
|
||||
}
|
||||
|
||||
export class GetUserByProviderAccountIdError extends UnknownError {
|
||||
name = "GetUserByProviderAccountIdError"
|
||||
}
|
||||
|
||||
export class UpdateUserError extends UnknownError {
|
||||
name = "UpdateUserError"
|
||||
}
|
||||
|
||||
export class DeleteUserError extends UnknownError {
|
||||
name = "DeleteUserError"
|
||||
}
|
||||
|
||||
export class LinkAccountError extends UnknownError {
|
||||
name = "LinkAccountError"
|
||||
}
|
||||
|
||||
export class UnlinkAccountError extends UnknownError {
|
||||
name = "UnlinkAccountError"
|
||||
}
|
||||
|
||||
export class CreateSessionError extends UnknownError {
|
||||
name = "CreateSessionError"
|
||||
}
|
||||
|
||||
export class GetSessionError extends UnknownError {
|
||||
name = "GetSessionError"
|
||||
}
|
||||
|
||||
export class UpdateSessionError extends UnknownError {
|
||||
name = "UpdateSessionError"
|
||||
}
|
||||
|
||||
export class DeleteSessionError extends UnknownError {
|
||||
name = "DeleteSessionError"
|
||||
}
|
||||
|
||||
export class CreateVerificationRequestError extends UnknownError {
|
||||
name = "CreateVerificationRequestError"
|
||||
}
|
||||
|
||||
export class GetVerificationRequestError extends UnknownError {
|
||||
name = "GetVerificationRequestError"
|
||||
}
|
||||
|
||||
export class DeleteVerificationRequestError extends UnknownError {
|
||||
name = "DeleteVerificationRequestError"
|
||||
}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import crypto from 'crypto'
|
||||
import jose from 'jose'
|
||||
import logger from './logger'
|
||||
import crypto from "crypto"
|
||||
import jose from "jose"
|
||||
import logger from "./logger"
|
||||
|
||||
// Set default algorithm to use for auto-generated signing key
|
||||
const DEFAULT_SIGNATURE_ALGORITHM = 'HS512'
|
||||
const DEFAULT_SIGNATURE_ALGORITHM = "HS512"
|
||||
|
||||
// Set default algorithm for auto-generated symmetric encryption key
|
||||
const DEFAULT_ENCRYPTION_ALGORITHM = 'A256GCM'
|
||||
const DEFAULT_ENCRYPTION_ALGORITHM = "A256GCM"
|
||||
|
||||
// Use encryption or not by default
|
||||
const DEFAULT_ENCRYPTION_ENABLED = false
|
||||
|
||||
const DEFAULT_MAX_AGE = 30 * 24 * 60 * 60 // 30 days
|
||||
|
||||
async function encode ({
|
||||
export async function encode({
|
||||
token = {},
|
||||
maxAge = DEFAULT_MAX_AGE,
|
||||
secret,
|
||||
signingKey,
|
||||
signingOptions = {
|
||||
expiresIn: `${maxAge}s`
|
||||
expiresIn: `${maxAge}s`,
|
||||
},
|
||||
encryptionKey,
|
||||
encryptionOptions = {
|
||||
alg: 'dir',
|
||||
alg: "dir",
|
||||
enc: DEFAULT_ENCRYPTION_ALGORITHM,
|
||||
zip: 'DEF'
|
||||
zip: "DEF",
|
||||
},
|
||||
encryption = DEFAULT_ENCRYPTION_ENABLED
|
||||
encryption = DEFAULT_ENCRYPTION_ENABLED,
|
||||
} = {}) {
|
||||
// Signing Key
|
||||
const _signingKey = signingKey
|
||||
@@ -49,7 +49,7 @@ async function encode ({
|
||||
return signedToken
|
||||
}
|
||||
|
||||
async function decode ({
|
||||
export async function decode({
|
||||
secret,
|
||||
token,
|
||||
maxAge = DEFAULT_MAX_AGE,
|
||||
@@ -57,14 +57,14 @@ async function decode ({
|
||||
verificationKey = signingKey, // Optional (defaults to encryptionKey)
|
||||
verificationOptions = {
|
||||
maxTokenAge: `${maxAge}s`,
|
||||
algorithms: [DEFAULT_SIGNATURE_ALGORITHM]
|
||||
algorithms: [DEFAULT_SIGNATURE_ALGORITHM],
|
||||
},
|
||||
encryptionKey,
|
||||
decryptionKey = encryptionKey, // Optional (defaults to encryptionKey)
|
||||
decryptionOptions = {
|
||||
algorithms: [DEFAULT_ENCRYPTION_ALGORITHM]
|
||||
algorithms: [DEFAULT_ENCRYPTION_ALGORITHM],
|
||||
},
|
||||
encryption = DEFAULT_ENCRYPTION_ENABLED
|
||||
encryption = DEFAULT_ENCRYPTION_ENABLED,
|
||||
} = {}) {
|
||||
if (!token) return null
|
||||
|
||||
@@ -77,8 +77,12 @@ async function decode ({
|
||||
: getDerivedEncryptionKey(secret)
|
||||
|
||||
// Decrypt token
|
||||
const decryptedToken = jose.JWE.decrypt(token, _encryptionKey, decryptionOptions)
|
||||
tokenToVerify = decryptedToken.toString('utf8')
|
||||
const decryptedToken = jose.JWE.decrypt(
|
||||
token,
|
||||
_encryptionKey,
|
||||
decryptionOptions
|
||||
)
|
||||
tokenToVerify = decryptedToken.toString("utf8")
|
||||
}
|
||||
|
||||
// Signing Key
|
||||
@@ -99,17 +103,22 @@ async function decode ({
|
||||
* raw?: boolean
|
||||
* }} params
|
||||
*/
|
||||
async function getToken (params) {
|
||||
export async function getToken(params) {
|
||||
const {
|
||||
req,
|
||||
// Use secure prefix for cookie name, unless URL is NEXTAUTH_URL is http://
|
||||
// or not set (e.g. development or test instance) case use unprefixed name
|
||||
secureCookie = !(!process.env.NEXTAUTH_URL || process.env.NEXTAUTH_URL.startsWith('http://')),
|
||||
cookieName = (secureCookie) ? '__Secure-next-auth.session-token' : 'next-auth.session-token',
|
||||
secureCookie = !(
|
||||
!process.env.NEXTAUTH_URL ||
|
||||
process.env.NEXTAUTH_URL.startsWith("http://")
|
||||
),
|
||||
cookieName = secureCookie
|
||||
? "__Secure-next-auth.session-token"
|
||||
: "next-auth.session-token",
|
||||
raw = false,
|
||||
decode: _decode = decode
|
||||
decode: _decode = decode,
|
||||
} = params
|
||||
if (!req) throw new Error('Must pass `req` to JWT getToken()')
|
||||
if (!req) throw new Error("Must pass `req` to JWT getToken()")
|
||||
|
||||
// Try to get token from cookie
|
||||
let token = req.cookies[cookieName]
|
||||
@@ -117,8 +126,8 @@ async function getToken (params) {
|
||||
// If cookie not found in cookie look for bearer token in authorization header.
|
||||
// This allows clients that pass through tokens in headers rather than as
|
||||
// cookies to use this helper function.
|
||||
if (!token && req.headers.authorization?.split(' ')[0] === 'Bearer') {
|
||||
const urlEncodedToken = req.headers.authorization.split(' ')[1]
|
||||
if (!token && req.headers.authorization?.split(" ")[0] === "Bearer") {
|
||||
const urlEncodedToken = req.headers.authorization.split(" ")[1]
|
||||
token = decodeURIComponent(urlEncodedToken)
|
||||
}
|
||||
|
||||
@@ -138,7 +147,7 @@ let DERIVED_SIGNING_KEY_WARNING = false
|
||||
let DERIVED_ENCRYPTION_KEY_WARNING = false
|
||||
|
||||
// Do the better hkdf of Node.js one added in `v15.0.0` and Third Party one
|
||||
function hkdf (secret, { byteLength, encryptionInfo, digest = 'sha256' }) {
|
||||
function hkdf(secret, { byteLength, encryptionInfo, digest = "sha256" }) {
|
||||
if (crypto.hkdfSync) {
|
||||
return Buffer.from(
|
||||
crypto.hkdfSync(
|
||||
@@ -150,39 +159,50 @@ function hkdf (secret, { byteLength, encryptionInfo, digest = 'sha256' }) {
|
||||
)
|
||||
)
|
||||
}
|
||||
return require('futoin-hkdf')(secret, byteLength, { info: encryptionInfo, hash: digest })
|
||||
return require("futoin-hkdf")(secret, byteLength, {
|
||||
info: encryptionInfo,
|
||||
hash: digest,
|
||||
})
|
||||
}
|
||||
|
||||
function getDerivedSigningKey (secret) {
|
||||
function getDerivedSigningKey(secret) {
|
||||
if (!DERIVED_SIGNING_KEY_WARNING) {
|
||||
logger.warn('JWT_AUTO_GENERATED_SIGNING_KEY')
|
||||
logger.warn("JWT_AUTO_GENERATED_SIGNING_KEY")
|
||||
DERIVED_SIGNING_KEY_WARNING = true
|
||||
}
|
||||
|
||||
const buffer = hkdf(secret, {
|
||||
byteLength: 64,
|
||||
encryptionInfo: 'NextAuth.js Generated Signing Key'
|
||||
encryptionInfo: "NextAuth.js Generated Signing Key",
|
||||
})
|
||||
const key = jose.JWK.asKey(buffer, {
|
||||
alg: DEFAULT_SIGNATURE_ALGORITHM,
|
||||
use: "sig",
|
||||
kid: "nextauth-auto-generated-signing-key",
|
||||
})
|
||||
const key = jose.JWK.asKey(buffer, { alg: DEFAULT_SIGNATURE_ALGORITHM, use: 'sig', kid: 'nextauth-auto-generated-signing-key' })
|
||||
return key
|
||||
}
|
||||
|
||||
function getDerivedEncryptionKey (secret) {
|
||||
function getDerivedEncryptionKey(secret) {
|
||||
if (!DERIVED_ENCRYPTION_KEY_WARNING) {
|
||||
logger.warn('JWT_AUTO_GENERATED_ENCRYPTION_KEY')
|
||||
logger.warn("JWT_AUTO_GENERATED_ENCRYPTION_KEY")
|
||||
DERIVED_ENCRYPTION_KEY_WARNING = true
|
||||
}
|
||||
|
||||
const buffer = hkdf(secret, {
|
||||
byteLength: 32,
|
||||
encryptionInfo: 'NextAuth.js Generated Encryption Key'
|
||||
encryptionInfo: "NextAuth.js Generated Encryption Key",
|
||||
})
|
||||
const key = jose.JWK.asKey(buffer, {
|
||||
alg: DEFAULT_ENCRYPTION_ALGORITHM,
|
||||
use: "enc",
|
||||
kid: "nextauth-auto-generated-encryption-key",
|
||||
})
|
||||
const key = jose.JWK.asKey(buffer, { alg: DEFAULT_ENCRYPTION_ALGORITHM, use: 'enc', kid: 'nextauth-auto-generated-encryption-key' })
|
||||
return key
|
||||
}
|
||||
|
||||
export default {
|
||||
encode,
|
||||
decode,
|
||||
getToken
|
||||
getToken,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import nodemailer from "nodemailer"
|
||||
import logger from "../lib/logger"
|
||||
import logger from '../lib/logger'
|
||||
import requireOptional from '@balazsorban/require-optional'
|
||||
|
||||
export default function Email(options) {
|
||||
return {
|
||||
@@ -22,34 +22,25 @@ export default function Email(options) {
|
||||
}
|
||||
}
|
||||
|
||||
const sendVerificationRequest = ({
|
||||
identifier: email,
|
||||
url,
|
||||
baseUrl,
|
||||
provider,
|
||||
}) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { server, from } = provider
|
||||
// Strip protocol from URL and use domain as site name
|
||||
const site = baseUrl.replace(/^https?:\/\//, "")
|
||||
|
||||
nodemailer.createTransport(server).sendMail(
|
||||
{
|
||||
async function sendVerificationRequest ({ identifier: email, url, baseUrl, provider }) {
|
||||
const { server, from } = provider
|
||||
// Strip protocol from URL and use domain as site name
|
||||
const site = baseUrl.replace(/^https?:\/\//, '')
|
||||
try {
|
||||
const nodemailer = requireOptional('nodemailer')
|
||||
await nodemailer
|
||||
.createTransport(server)
|
||||
.sendMail({
|
||||
to: email,
|
||||
from,
|
||||
subject: `Sign in to ${site}`,
|
||||
text: text({ url, site, email }),
|
||||
html: html({ url, site, email }),
|
||||
},
|
||||
(error) => {
|
||||
if (error) {
|
||||
logger.error("SEND_VERIFICATION_EMAIL_ERROR", email, error)
|
||||
return reject(new Error("SEND_VERIFICATION_EMAIL_ERROR", error))
|
||||
}
|
||||
return resolve()
|
||||
}
|
||||
)
|
||||
})
|
||||
html: html({ url, site, email })
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('SEND_VERIFICATION_EMAIL_ERROR', email, error)
|
||||
throw new Error('SEND_VERIFICATION_EMAIL_ERROR')
|
||||
}
|
||||
}
|
||||
|
||||
// Email HTML body
|
||||
|
||||
22
src/providers/mailchimp.js
Normal file
22
src/providers/mailchimp.js
Normal file
@@ -0,0 +1,22 @@
|
||||
export default function Mailchimp(options) {
|
||||
return {
|
||||
id: 'mailchimp',
|
||||
name: 'Mailchimp',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: '',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://login.mailchimp.com/oauth2/token',
|
||||
authorizationUrl: 'https://login.mailchimp.com/oauth2/authorize?response_type=code',
|
||||
profileUrl: 'https://login.mailchimp.com/oauth2/metadata',
|
||||
profile: (profile) => {
|
||||
return {
|
||||
id: profile.login.login_id,
|
||||
name: profile.accountname,
|
||||
email: profile.login.email,
|
||||
image: null
|
||||
}
|
||||
},
|
||||
...options
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import adapters from '../adapters'
|
||||
import jwt from '../lib/jwt'
|
||||
import parseUrl from '../lib/parse-url'
|
||||
import logger, { setLogger } from '../lib/logger'
|
||||
@@ -15,6 +14,8 @@ import csrfTokenHandler from './lib/csrf-token-handler'
|
||||
import * as pkce from './lib/oauth/pkce-handler'
|
||||
import * as state from './lib/oauth/state-handler'
|
||||
|
||||
import optionalRequire from '@balazsorban/require-optional'
|
||||
|
||||
// To work properly in production with OAuth providers the NEXTAUTH_URL
|
||||
// environment variable must be set.
|
||||
if (!process.env.NEXTAUTH_URL) {
|
||||
@@ -73,7 +74,7 @@ async function NextAuthHandler (req, res, userOptions) {
|
||||
// Protection only works on OAuth 2.x providers
|
||||
if (provider?.type === 'oauth' && provider.version?.startsWith('2')) {
|
||||
// When provider.state is undefined, we still want this to pass
|
||||
if (!provider.protection && provider.state !== false) {
|
||||
if (!provider.protection) {
|
||||
// Default to state, as we did in 3.1 REVIEW: should we use "pkce" or "none" as default?
|
||||
provider.protection = ['state']
|
||||
} else if (typeof provider.protection === 'string') {
|
||||
@@ -86,7 +87,11 @@ async function NextAuthHandler (req, res, userOptions) {
|
||||
// Parse database / adapter
|
||||
// If adapter is provided, use it (advanced usage, overrides database)
|
||||
// If database URI or config object is provided, use it (simple usage)
|
||||
const adapter = userOptions.adapter ?? (userOptions.database && adapters.Default(userOptions.database))
|
||||
let adapter = userOptions.adapter
|
||||
if ((!adapter && !!userOptions.database)) {
|
||||
const TypeOrm = optionalRequire('../adapters/typeorm')
|
||||
adapter = TypeOrm.Adapter(userOptions.database)
|
||||
}
|
||||
|
||||
// User provided options are overriden by other options,
|
||||
// except for the options with special handling above
|
||||
|
||||
@@ -65,18 +65,13 @@ export default async function callback (req, res) {
|
||||
|
||||
try {
|
||||
const signInCallbackResponse = await callbacks.signIn(userOrProfile, account, OAuthProfile)
|
||||
if (signInCallbackResponse === false) {
|
||||
if (!signInCallbackResponse) {
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=AccessDenied`)
|
||||
} else if (typeof signInCallbackResponse === 'string') {
|
||||
return res.redirect(signInCallbackResponse)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=${encodeURIComponent(error.message)}`)
|
||||
}
|
||||
// TODO: Remove in a future major release
|
||||
logger.warn('SIGNIN_CALLBACK_REJECT_REDIRECT')
|
||||
return res.redirect(error)
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=${encodeURIComponent(error.message)}`)
|
||||
}
|
||||
|
||||
// Sign user in
|
||||
@@ -161,18 +156,13 @@ export default async function callback (req, res) {
|
||||
// Check if user is allowed to sign in
|
||||
try {
|
||||
const signInCallbackResponse = await callbacks.signIn(profile, account, { email })
|
||||
if (signInCallbackResponse === false) {
|
||||
if (!signInCallbackResponse) {
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=AccessDenied`)
|
||||
} else if (typeof signInCallbackResponse === 'string') {
|
||||
return res.redirect(signInCallbackResponse)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=${encodeURIComponent(error.message)}`)
|
||||
}
|
||||
// TODO: Remove in a future major release
|
||||
logger.warn('SIGNIN_CALLBACK_REJECT_REDIRECT')
|
||||
return res.redirect(error)
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=${encodeURIComponent(error.message)}`)
|
||||
}
|
||||
|
||||
// Sign user in
|
||||
@@ -236,12 +226,11 @@ export default async function callback (req, res) {
|
||||
userObjectReturnedFromAuthorizeHandler = await provider.authorize(credentials)
|
||||
if (!userObjectReturnedFromAuthorizeHandler) {
|
||||
return res.status(401).redirect(`${baseUrl}${basePath}/error?error=CredentialsSignin&provider=${encodeURIComponent(provider.id)}`)
|
||||
} else if (typeof userObjectReturnedFromAuthorizeHandler === 'string') {
|
||||
return res.redirect(userObjectReturnedFromAuthorizeHandler)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=${encodeURIComponent(error.message)}`)
|
||||
}
|
||||
return res.redirect(error)
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=${encodeURIComponent(error.message)}`)
|
||||
}
|
||||
|
||||
const user = userObjectReturnedFromAuthorizeHandler
|
||||
@@ -249,14 +238,13 @@ export default async function callback (req, res) {
|
||||
|
||||
try {
|
||||
const signInCallbackResponse = await callbacks.signIn(user, account, credentials)
|
||||
if (signInCallbackResponse === false) {
|
||||
if (!signInCallbackResponse) {
|
||||
return res.status(403).redirect(`${baseUrl}${basePath}/error?error=AccessDenied`)
|
||||
} else if (typeof signInCallbackResponse === 'string') {
|
||||
return res.redirect(signInCallbackResponse)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=${encodeURIComponent(error.message)}`)
|
||||
}
|
||||
return res.redirect(error)
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=${encodeURIComponent(error.message)}`)
|
||||
}
|
||||
|
||||
const defaultJwtPayload = {
|
||||
|
||||
@@ -45,18 +45,13 @@ export default async function signin (req, res) {
|
||||
// Check if user is allowed to sign in
|
||||
try {
|
||||
const signInCallbackResponse = await callbacks.signIn(profile, account, { email, verificationRequest: true })
|
||||
if (signInCallbackResponse === false) {
|
||||
if (!signInCallbackResponse) {
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=AccessDenied`)
|
||||
} else if (typeof signInCallbackResponse === 'string') {
|
||||
return res.redirect(signInCallbackResponse)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=${encodeURIComponent(error)}`)
|
||||
}
|
||||
// TODO: Remove in a future major release
|
||||
logger.warn('SIGNIN_CALLBACK_REJECT_REDIRECT')
|
||||
return res.redirect(error)
|
||||
return res.redirect(`${baseUrl}${basePath}/error?error=${encodeURIComponent(error)}`)
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
1
types/adapters.d.ts
vendored
1
types/adapters.d.ts
vendored
@@ -204,6 +204,7 @@ declare class TypeORMUserModel implements User {
|
||||
image?: string,
|
||||
emailVerified?: Date
|
||||
)
|
||||
[x: string]: unknown
|
||||
}
|
||||
|
||||
declare class TypeORMSessionModel implements Session {
|
||||
|
||||
23
types/errors.d.ts
vendored
Normal file
23
types/errors.d.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Same as the default `Error`, but it is JSON serializable.
|
||||
* @source https://iaincollins.medium.com/error-handling-in-javascript-a6172ccdf9af
|
||||
*/
|
||||
export class UnknownError extends Error {}
|
||||
export class OAuthCallbackError extends UnknownError {}
|
||||
export class AccountNotLinkedError extends UnknownError {}
|
||||
export class CreateUserError extends UnknownError {}
|
||||
export class GetUserError extends UnknownError {}
|
||||
export class GetUserByEmailError extends UnknownError {}
|
||||
export class GetUserByIdError extends UnknownError {}
|
||||
export class GetUserByProviderAccountIdError extends UnknownError {}
|
||||
export class UpdateUserError extends UnknownError {}
|
||||
export class DeleteUserError extends UnknownError {}
|
||||
export class LinkAccountError extends UnknownError {}
|
||||
export class UnlinkAccountError extends UnknownError {}
|
||||
export class CreateSessionError extends UnknownError {}
|
||||
export class GetSessionError extends UnknownError {}
|
||||
export class UpdateSessionError extends UnknownError {}
|
||||
export class DeleteSessionError extends UnknownError {}
|
||||
export class CreateVerificationRequestError extends UnknownError {}
|
||||
export class GetVerificationRequestError extends UnknownError {}
|
||||
export class DeleteVerificationRequestError extends UnknownError {}
|
||||
18
types/index.d.ts
vendored
18
types/index.d.ts
vendored
@@ -251,14 +251,16 @@ export interface Account extends TokenSet, Record<string, unknown> {
|
||||
type: string
|
||||
}
|
||||
|
||||
/** The OAuth profile returned from your provider */
|
||||
export interface Profile extends Record<string, unknown> {
|
||||
export interface DefaultProfile {
|
||||
sub?: string
|
||||
name?: string
|
||||
email?: string
|
||||
image?: string
|
||||
}
|
||||
|
||||
/** The OAuth profile returned from your provider */
|
||||
export interface Profile extends Record<string, unknown>, DefaultProfile {}
|
||||
|
||||
/** [Documentation](https://next-auth.js.org/configuration/callbacks) */
|
||||
export interface CallbacksOptions<
|
||||
P extends Record<string, unknown> = Profile,
|
||||
@@ -391,6 +393,12 @@ export interface SessionOptions {
|
||||
updateAge?: number
|
||||
}
|
||||
|
||||
export interface DefaultUser {
|
||||
name?: string | null
|
||||
email?: string | null
|
||||
image?: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* The shape of the returned object in the OAuth providers' `profile` callback,
|
||||
* available in the `jwt` and `session` callbacks,
|
||||
@@ -401,11 +409,7 @@ export interface SessionOptions {
|
||||
* [`jwt` callback](https://next-auth.js.org/configuration/callbacks#jwt-callback) |
|
||||
* [`profile` OAuth provider callback](https://next-auth.js.org/configuration/providers#using-a-custom-provider)
|
||||
*/
|
||||
export interface User {
|
||||
name?: string | null
|
||||
email?: string | null
|
||||
image?: string | null
|
||||
}
|
||||
export interface User extends Record<string, unknown>, DefaultUser {}
|
||||
|
||||
declare function NextAuth(
|
||||
req: NextApiRequest,
|
||||
|
||||
27
types/internals/index.d.ts
vendored
27
types/internals/index.d.ts
vendored
@@ -14,9 +14,22 @@ export type NextAuthSharedOptions =
|
||||
| "theme"
|
||||
| "debug"
|
||||
| "logger"
|
||||
| "session"
|
||||
|
||||
export interface AppOptions
|
||||
extends Pick<NextAuthOptions, NextAuthSharedOptions> {
|
||||
extends Required<Pick<NextAuthOptions, NextAuthSharedOptions>> {
|
||||
providers: AppProvider[]
|
||||
baseUrl: string
|
||||
basePath: string
|
||||
action:
|
||||
| "providers"
|
||||
| "session"
|
||||
| "csrf"
|
||||
| "signin"
|
||||
| "signout"
|
||||
| "callback"
|
||||
| "verify-request"
|
||||
| "error"
|
||||
pkce?: {
|
||||
code_verifier?: string
|
||||
/**
|
||||
@@ -27,18 +40,6 @@ export interface AppOptions
|
||||
code_challenge_method?: "S256"
|
||||
}
|
||||
provider?: AppProvider
|
||||
providers: AppProvider[]
|
||||
baseUrl?: string
|
||||
basePath?: string
|
||||
action?:
|
||||
| "providers"
|
||||
| "session"
|
||||
| "csrf"
|
||||
| "signin"
|
||||
| "signout"
|
||||
| "callback"
|
||||
| "verify-request"
|
||||
| "error"
|
||||
csrfToken?: string
|
||||
csrfTokenVerified?: boolean
|
||||
}
|
||||
|
||||
1
types/providers.d.ts
vendored
1
types/providers.d.ts
vendored
@@ -77,6 +77,7 @@ export type OAuthProviderType =
|
||||
| "Kakao"
|
||||
| "LINE"
|
||||
| "LinkedIn"
|
||||
| "Mailchimp"
|
||||
| "MailRu"
|
||||
| "Medium"
|
||||
| "Netlify"
|
||||
|
||||
@@ -60,7 +60,7 @@ By default, the built-in pages will follow the system theme, utilizing the [`pre
|
||||
In order to get the available authentication providers and the URLs to use for them, you can make a request to the API endpoint `/api/auth/providers`:
|
||||
|
||||
```jsx title="pages/auth/signin.js"
|
||||
import { providers, signIn } from 'next-auth/client'
|
||||
import { getProviders, signIn } from 'next-auth/client'
|
||||
|
||||
export default function SignIn({ providers }) {
|
||||
return (
|
||||
@@ -76,7 +76,7 @@ export default function SignIn({ providers }) {
|
||||
|
||||
// This is the recommended way for Next.js 9.3 or newer
|
||||
export async function getServerSideProps(context){
|
||||
const providers = await providers()
|
||||
const providers = await getProviders()
|
||||
return {
|
||||
props: { providers }
|
||||
}
|
||||
@@ -86,7 +86,7 @@ export async function getServerSideProps(context){
|
||||
// If older than Next.js 9.3
|
||||
SignIn.getInitialProps = async () => {
|
||||
return {
|
||||
providers: await providers()
|
||||
providers: await getProviders()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -76,7 +76,6 @@ In _most cases_ it does not make sense to specify a database in NextAuth.js opti
|
||||
The provider you tried to use failed when setting [PKCE or Proof Key for Code Exchange](https://tools.ietf.org/html/rfc7636#section-4.2).
|
||||
The `code_verifier` is saved in a cookie called (by default) `__Secure-next-auth.pkce.code_verifier` which expires after 15 minutes.
|
||||
Check if `cookies.pkceCodeVerifier` is configured correctly. The default `code_challenge_method` is `"S256"`. This is currently not configurable to `"plain"`, as it is not recommended, and in most cases it is only supported for backward compatibility.
|
||||
|
||||
---
|
||||
|
||||
### Session Handling
|
||||
|
||||
26
www/docs/providers/mailchimp.md
Normal file
26
www/docs/providers/mailchimp.md
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
id: mailchimp
|
||||
title: Mailchimp
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
https://mailchimp.com/developer/marketing/guides/access-user-data-oauth-2/
|
||||
|
||||
## Configuration
|
||||
|
||||
https://admin.mailchimp.com/account/oauth2/client/
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import Providers from `next-auth/providers`
|
||||
...
|
||||
providers: [
|
||||
Providers.Mailchimp({
|
||||
clientId: process.env.MAILCHIMP_CLIENT_ID,
|
||||
clientSecret: process.env.MAILCHIMP_CLIENT_SECRET
|
||||
})
|
||||
]
|
||||
...
|
||||
```
|
||||
@@ -188,7 +188,7 @@ npx prisma generate
|
||||
To configure you database to use the new schema (i.e. create tables and columns) use the `prisma migrate` command:
|
||||
|
||||
```
|
||||
npx prisma migrate dev --preview-feature
|
||||
npx prisma migrate dev
|
||||
```
|
||||
|
||||
To generate a schema in this way with the above example code, you will need to specify your datbase connection string in the environment variable `DATABASE_URL`. You can do this by setting it in a `.env` file at the root of your project.
|
||||
|
||||
@@ -11,7 +11,7 @@ _New submissions and edits are welcome!_
|
||||
|
||||
### [NextJS Authentication Crash Course with NextAuth.js](https://youtu.be/o_wZIVmWteQ)
|
||||
|
||||
This tutorial dives in to the ins and outs of NextAuth including email, GitHub, Twitter and integrating with Auth0 in under hour.
|
||||
This tutorial dives in to the ins and outs of NextAuth including email, Github, Twitter and integrating with Auth0 in under hour.
|
||||
|
||||
### [Create your own NextAuth.js Login Pages](https://youtu.be/kB6YNYZ63fw)
|
||||
|
||||
|
||||
@@ -46,32 +46,4 @@ You can use [node-jose-tools](https://www.npmjs.com/package/node-jose-tools) to
|
||||
|
||||
**Option 2**: Specify custom encode/decode functions on the jwt object. This gives you complete control over signing / verification / etc.
|
||||
|
||||
#### JWT_AUTO_GENERATED_ENCRYPTION_KEY
|
||||
|
||||
#### SIGNIN_CALLBACK_REJECT_REDIRECT
|
||||
|
||||
You returned something in the `signIn` callback, that is being deprecated.
|
||||
|
||||
You probably had something similar in the callback:
|
||||
```js
|
||||
return Promise.reject("/some/url")
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```js
|
||||
throw "/some/url"
|
||||
```
|
||||
|
||||
To remedy this, simply return the url instead:
|
||||
|
||||
```js
|
||||
return "/some/url"
|
||||
```
|
||||
|
||||
|
||||
#### STATE_OPTION_DEPRECATION
|
||||
You provided `state: true` or `state: false` as a provider option. This is being deprecated in a later release in favour of `protection: "state"` and `protection: "none"` respectively. To remedy this warning:
|
||||
|
||||
- If you use `state: true`, just simply remove it. The default is `protection: "state"` already..
|
||||
- If you use `state: false`, set `protection: "none"`.
|
||||
#### JWT_AUTO_GENERATED_ENCRYPTION_KEY
|
||||
Reference in New Issue
Block a user