Compare commits

...

4 Commits

Author SHA1 Message Date
Balázs Orbán
543f812eb3 fix(build): export functions in jwt (#1814) 2021-04-22 19:28:17 +02:00
Joël Galeran
0c9f9777c5 docs(adapter): Remove --preview-feature flag (#1807)
* Remove --preview-feature flag

* Update [...nextauth].js
2021-04-22 18:11:30 +02:00
Balázs Orbán
34f334a71d fix(ts): make Profile/User interfaces overridable (#1801)
* fix(ts): create DefaultUser interface

* fix(ts): fix TypeORMUserModel

* fix(ts): create DefaultProfile
2021-04-22 01:04:23 +02:00
Balázs Orbán
172ad02f8c fix(ts): move AppProvider out of internals (#1800)
* fix(ts): move AppProvider out of internals

* fix(ts): fix import paths
2021-04-21 23:09:42 +02:00
9 changed files with 76 additions and 53 deletions

View File

@@ -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 })
})

View File

@@ -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,
}

3
types/adapters.d.ts vendored
View File

@@ -1,7 +1,7 @@
import { AppOptions } from "./internals"
import { ConnectionOptions, EntitySchema } from "typeorm"
import { User } from "."
import { AppProvider } from "./internals/providers"
import { AppProvider } from "./providers"
export interface Profile {
id: string
@@ -204,6 +204,7 @@ declare class TypeORMUserModel implements User {
image?: string,
emailVerified?: Date
)
[x: string]: unknown
}
declare class TypeORMSessionModel implements Session {

18
types/index.d.ts vendored
View File

@@ -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,

View File

@@ -1,6 +1,6 @@
import { NextApiRequest, NextApiResponse } from "./utils"
import { NextAuthOptions } from ".."
import { AppProvider } from "./providers"
import { AppProvider } from "../providers"
/** Options that are the same both in internal and user provided options. */
export type NextAuthSharedOptions =

View File

@@ -1,6 +0,0 @@
import { CommonProviderOptions } from "../providers"
export interface AppProvider extends CommonProviderOptions {
signinUrl: string
callbackUrl: string
}

View File

@@ -161,6 +161,11 @@ export type AppProviders = Array<
Provider | ReturnType<BuiltInProviders[keyof BuiltInProviders]>
>
export interface AppProvider extends CommonProviderOptions {
signinUrl: string
callbackUrl: string
}
declare const Providers: BuiltInProviders
export default Providers

View File

@@ -1,4 +1,4 @@
import Providers, { OAuthConfig } from "next-auth/providers"
import Providers, { AppProvider, OAuthConfig } from "next-auth/providers"
import {
Adapter,
EmailAppProvider,
@@ -12,7 +12,6 @@ import * as JWTType from "next-auth/jwt"
import { Socket } from "net"
import { NextApiRequest, NextApiResponse } from "internals/utils"
import { AppOptions } from "internals"
import { AppProvider } from "internals/providers"
const req: NextApiRequest = Object.assign(new IncomingMessage(new Socket()), {
query: {},

View File

@@ -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.