Compare commits

...

17 Commits

Author SHA1 Message Date
Nico Domino
eba79f4445 feat: upgrade docusaurus + style a bit (#1993) 2021-05-13 23:24:02 +02:00
Balázs Orbán
e3bb9881ea chore(dev): fix dev app imports (#1991) 2021-05-13 12:36:28 +02:00
Balázs Orbán
827049cb35 docs(www): Docusaurus webpack 5 (#1989)
This reverts commit bc9805d1ba.
2021-05-13 01:28:36 +02:00
Nico Domino
ad8100d402 docs: max cookie size information (#1949)
* fix: max cookie information

* fix: typo

* fix: wording regarding cookie size

* Update www/docs/faq.md

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-05-12 10:22:08 +02:00
dependabot[bot]
7b5defff16 chore(deps): bump hosted-git-info from 2.8.8 to 2.8.9 (#1976)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-05-12 10:20:24 +02:00
Balázs Orbán
bc9805d1ba docs: revert "Docusaurus webpack 5" (#1982)
This reverts commit c823016b36.
2021-05-12 10:19:30 +02:00
Sébastien Lorber
c823016b36 docs(www): update Docusaurus to webpack 5 (#1826)
* upgrade

* upgrade

* fix lunr plugin bug

Co-authored-by: Lluis Agusti <hi@llu.lu>
2021-05-12 10:01:33 +02:00
dependabot[bot]
ca0f4c6fba chore(deps): bump hosted-git-info from 2.8.8 to 2.8.9 in /www (#1977)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-11 21:04:27 +02:00
Balázs Orbán
c0d2f2d852 fix(adapter): upgrade legacy adapters (#1952)
* refactor(adapter): upgrade typeorm-legacy-adapter

* fix(ts): correct exported typeorm types

* fix(adapter): correct adapter exports

* chore(deps): upgrade typeorm-legacy-adapter

* chore(deps): upgrade dependencies

* chore: match comment for legacy adapters

* fix(ts): correctly export Prisma legacy types

* chore(deps): upgrade prisma legacy adapter

* chore(deps): remove unused dependencies

* test(ts): only run TS tests on latest TS version

* chore(deps): remove unused dev dependencies

* chore(deps): upgrade prisma adapter
2021-05-11 00:15:01 +02:00
Balázs Orbán
71f63117a9 fix(oauth): correctly set internal protection value (#1962) 2021-05-09 23:00:06 +02:00
i-palindrome-i
d04ce29314 feat(provider): add WorkOS provider (#1939)
* feat(provider): add WorkOS provider

* Update www/docs/providers/workos.md

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Update workos.md

Co-authored-by: Adam Kaczmarek <adamkaz+workos@gmail.com>
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-05-09 21:45:37 +02:00
Lluis Agusti
d2882f1958 fix(deps): unpin react-dom version (#1956) 2021-05-09 21:43:51 +02:00
Lluis Agusti
66db563ca5 docs(provider): link to providers' source code (#1955) 2021-05-09 21:41:28 +02:00
Marcus Reinhardt
9619077363 docs(typeorm): update link to source (#1957) 2021-05-08 23:37:55 +02:00
dependabot[bot]
013ccb4cb0 chore(deps): bump lodash from 4.17.19 to 4.17.21 (#1954)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-08 00:40:35 +02:00
dependabot[bot]
6eb41259d1 chore(deps): bump underscore from 1.10.2 to 1.13.1 (#1951)
Bumps [underscore](https://github.com/jashkenas/underscore) from 1.10.2 to 1.13.1.
- [Release notes](https://github.com/jashkenas/underscore/releases)
- [Commits](https://github.com/jashkenas/underscore/compare/1.10.2...1.13.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-07 17:58:22 +02:00
Nico Domino
141f8d07e2 docs(provider): clarify where user is created with email provider (#1950) 2021-05-07 00:54:52 +02:00
66 changed files with 17959 additions and 9438 deletions

View File

@@ -1,5 +1,9 @@
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import NextAuth from "next-auth"
import EmailProvider from "next-auth/providers/email"
import GitHubProvider from "next-auth/providers/github"
import Auth0Provider from "next-auth/providers/auth0"
import TwitterProvider from "next-auth/providers/twitter"
import CredentialsProvider from "next-auth/providers/credentials"
// import Adapters from 'next-auth/adapters'
// import { PrismaClient } from '@prisma/client'
@@ -28,15 +32,15 @@ export default NextAuth({
// }
// },
providers: [
Providers.Email({
EmailProvider({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM
from: process.env.EMAIL_FROM,
}),
Providers.GitHub({
GitHubProvider({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET
clientSecret: process.env.GITHUB_SECRET,
}),
Providers.Auth0({
Auth0Provider({
clientId: process.env.AUTH0_ID,
clientSecret: process.env.AUTH0_SECRET,
domain: process.env.AUTH0_DOMAIN,
@@ -45,36 +49,36 @@ export default NextAuth({
// authorizationParams: {
// response_mode: 'form_post'
// }
protection: 'pkce'
protection: "pkce",
}),
Providers.Twitter({
TwitterProvider({
clientId: process.env.TWITTER_ID,
clientSecret: process.env.TWITTER_SECRET
clientSecret: process.env.TWITTER_SECRET,
}),
Providers.Credentials({
name: 'Credentials',
CredentialsProvider({
name: "Credentials",
credentials: {
password: { label: 'Password', type: 'password' }
password: { label: "Password", type: "password" },
},
async authorize (credentials) {
if (credentials.password === 'password') {
async authorize(credentials) {
if (credentials.password === "password") {
return {
id: 1,
name: 'Fill Murray',
email: 'bill@fillmurray.com',
image: 'https://www.fillmurray.com/64/64'
name: "Fill Murray",
email: "bill@fillmurray.com",
image: "https://www.fillmurray.com/64/64",
}
}
return null
}
})
},
}),
],
jwt: {
encryption: true,
secret: process.env.SECRET
secret: process.env.SECRET,
},
debug: false,
theme: 'auto'
theme: "auto",
// Default Database Adapter (TypeORM)
// database: process.env.DATABASE_URL

1765
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -38,7 +38,7 @@
"watch:js": "babel --config-file ./config/babel.config.js --watch src --out-dir dist",
"watch:css": "postcss --config config/postcss.config.js --watch src/**/*.css --base src --dir dist",
"test": "echo \"Write some tests...\"; npm run test:types",
"test:types": "dtslint types",
"test:types": "dtslint types --onlyTestTsNext",
"prepublishOnly": "npm run build",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
@@ -64,7 +64,6 @@
"@babel/runtime": "^7.14.0",
"@next-auth/prisma-legacy-adapter": "canary",
"@next-auth/typeorm-legacy-adapter": "canary",
"crypto-js": "^4.0.0",
"futoin-hkdf": "^1.3.2",
"jose": "^1.27.2",
"jsonwebtoken": "^8.5.1",
@@ -73,13 +72,11 @@
"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"
"querystring": "^0.2.0"
},
"peerDependencies": {
"react": "^16.13.1 || ^17",
"react-dom": "16.13.1 || ^17"
"react-dom": "^16.13.1 || ^17"
},
"peerOptionalDependencies": {
"mongodb": "^3.5.9",
@@ -94,7 +91,6 @@
"@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/plugin-transform-runtime": "^7.13.15",
"@babel/preset-env": "^7.9.6",
"@prisma/client": "^2.16.1",
"@semantic-release/commit-analyzer": "^8.0.1",
"@semantic-release/github": "^7.2.0",
"@semantic-release/npm": "7.0.8",
@@ -115,19 +111,10 @@
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.3.1",
"eslint-plugin-standard": "^5.0.0",
"mocha": "^8.1.3",
"mongodb": "^3.5.9",
"mssql": "^6.2.1",
"mysql": "^2.18.1",
"next": "^10.0.5",
"pg": "^8.2.1",
"postcss-cli": "^7.1.1",
"postcss-nested": "^4.2.1",
"prettier": "^2.2.1",
"prisma": "^2.16.1",
"puppeteer": "^5.2.1",
"puppeteer-extra": "^3.1.15",
"puppeteer-extra-plugin-stealth": "^2.6.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"typescript": "^4.1.3"

View File

@@ -1,8 +1,10 @@
import TypeORM from './typeorm'
import Prisma from './prisma'
import * as TypeORM from "./typeorm"
import * as Prisma from "./prisma"
export { TypeORM, Prisma }
export default {
Default: TypeORM.Adapter,
TypeORM,
Prisma
Prisma,
}

View File

@@ -1,8 +1,6 @@
/*
* Source code is now at:
* Source code can be found at:
* https://github.com/nextauthjs/adapters/tree/canary/packages/prisma-legacy
*/
import PrismaLegacyAdapter from "@next-auth/prisma-legacy-adapter"
export default PrismaLegacyAdapter
export { PrismaLegacyAdapter as Adapter } from "@next-auth/prisma-legacy-adapter"

View File

@@ -1,8 +1,9 @@
/*
* Source code is now at:
* Source code can be found at:
* https://github.com/nextauthjs/adapters/tree/canary/packages/typeorm-legacy
*/
import TypeORMLegacyAdapter from "@next-auth/typeorm-legacy-adapter"
export default TypeORMLegacyAdapter
export {
TypeORMLegacyAdapter as Adapter,
Models,
} from "@next-auth/typeorm-legacy-adapter"

24
src/providers/workos.js Normal file
View File

@@ -0,0 +1,24 @@
export default function WorkOS(options) {
return {
id: 'workos',
name: 'WorkOS',
type: 'oauth',
version: '2.0',
scope: '',
params: {
grant_type: 'authorization_code',
client_id: options.clientId,
client_secret: options.clientSecret
},
accessTokenUrl: 'https://api.workos.com/sso/token/',
authorizationUrl: `https://api.workos.com/sso/authorize/?response_type=code&domain=${options.domain}`,
profileUrl: 'https://api.workos.com/sso/profile/',
profile: (profile) => {
return {
...profile,
name: `${profile.first_name} ${profile.last_name}`
}
},
...options
}
}

View File

@@ -1,24 +1,24 @@
import adapters from '../adapters'
import jwt from '../lib/jwt'
import parseUrl from '../lib/parse-url'
import logger, { setLogger } from '../lib/logger'
import * as cookie from './lib/cookie'
import * as defaultEvents from './lib/default-events'
import * as defaultCallbacks from './lib/default-callbacks'
import parseProviders from './lib/providers'
import * as routes from './routes'
import renderPage from './pages'
import createSecret from './lib/create-secret'
import callbackUrlHandler from './lib/callback-url-handler'
import extendRes from './lib/extend-res'
import csrfTokenHandler from './lib/csrf-token-handler'
import * as pkce from './lib/oauth/pkce-handler'
import * as state from './lib/oauth/state-handler'
import adapters from "../adapters"
import jwt from "../lib/jwt"
import parseUrl from "../lib/parse-url"
import logger, { setLogger } from "../lib/logger"
import * as cookie from "./lib/cookie"
import * as defaultEvents from "./lib/default-events"
import * as defaultCallbacks from "./lib/default-callbacks"
import parseProviders from "./lib/providers"
import * as routes from "./routes"
import renderPage from "./pages"
import createSecret from "./lib/create-secret"
import callbackUrlHandler from "./lib/callback-url-handler"
import extendRes from "./lib/extend-res"
import csrfTokenHandler from "./lib/csrf-token-handler"
import * as pkce from "./lib/oauth/pkce-handler"
import * as state from "./lib/oauth/state-handler"
// To work properly in production with OAuth providers the NEXTAUTH_URL
// environment variable must be set.
if (!process.env.NEXTAUTH_URL) {
logger.warn('NEXTAUTH_URL', 'NEXTAUTH_URL environment variable not set')
logger.warn("NEXTAUTH_URL", "NEXTAUTH_URL environment variable not set")
}
/**
@@ -26,7 +26,7 @@ if (!process.env.NEXTAUTH_URL) {
* @param {import("next").NextApiResponse} res
* @param {import("types").NextAuthOptions} userOptions
*/
async function NextAuthHandler (req, res, userOptions) {
async function NextAuthHandler(req, res, userOptions) {
if (userOptions.logger) {
setLogger(userOptions.logger)
}
@@ -39,13 +39,15 @@ async function NextAuthHandler (req, res, userOptions) {
// to avoid early termination of calls to the serverless function
// (and then return that promise when we are done) - eslint
// complains but I'm not sure there is another way to do this.
return new Promise(async resolve => { // eslint-disable-line no-async-promise-executor
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
extendRes(req, res, resolve)
if (!req.query.nextauth) {
const error = 'Cannot find [...nextauth].js in pages/api/auth. Make sure the filename is written correctly.'
const error =
"Cannot find [...nextauth].js in pages/api/auth. Make sure the filename is written correctly."
logger.error('MISSING_NEXTAUTH_API_ROUTE_ERROR', error)
logger.error("MISSING_NEXTAUTH_API_ROUTE_ERROR", error)
return res.status(500).end(`Error: ${error}`)
}
@@ -53,31 +55,48 @@ async function NextAuthHandler (req, res, userOptions) {
nextauth,
action = nextauth[0],
providerId = nextauth[1],
error = nextauth[1]
error = nextauth[1],
} = req.query
// @todo refactor all existing references to baseUrl and basePath
const { basePath, baseUrl } = parseUrl(process.env.NEXTAUTH_URL || process.env.VERCEL_URL)
const { basePath, baseUrl } = parseUrl(
process.env.NEXTAUTH_URL || process.env.VERCEL_URL
)
const cookies = {
...cookie.defaultCookies(userOptions.useSecureCookies || baseUrl.startsWith('https://')),
...cookie.defaultCookies(
userOptions.useSecureCookies || baseUrl.startsWith("https://")
),
// Allow user cookie options to override any cookie settings above
...userOptions.cookies
...userOptions.cookies,
}
const secret = createSecret({ userOptions, basePath, baseUrl })
const providers = parseProviders({ providers: userOptions.providers, baseUrl, basePath })
const providers = parseProviders({
providers: userOptions.providers,
baseUrl,
basePath,
})
const provider = providers.find(({ id }) => id === providerId)
// 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) {
// 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') {
provider.protection = [provider.protection]
// TODO:
// - rename to `checks` in 4.x, so it is similar to `openid-client`
// - stop supporting `protection` as string
// - remove `state` property
if (provider?.type === "oauth" && provider.version?.startsWith("2")) {
// Priority: (protection array > protection string) > state > default
if (provider.protection) {
provider.protection = Array.isArray(provider.protection)
? provider.protection
: [provider.protection]
} else if (provider.state !== undefined) {
provider.protection = [provider.state ? "state" : "none"]
} else {
// Default to state, as we did in 3.1
// REVIEW: should we use "pkce" or "none" as default?
provider.protection = ["state"]
}
}
@@ -86,14 +105,16 @@ 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))
const adapter =
userOptions.adapter ??
(userOptions.database && adapters.Default(userOptions.database))
// User provided options are overriden by other options,
// except for the options with special handling above
req.options = {
debug: false,
pages: {},
theme: 'auto',
theme: "auto",
// Custom options override defaults
...userOptions,
// These computed settings can have values in userOptions but we override them
@@ -111,7 +132,7 @@ async function NextAuthHandler (req, res, userOptions) {
jwt: !adapter, // If no adapter specified, force use of JSON Web Tokens (stateless)
maxAge,
updateAge: 24 * 60 * 60, // Sessions updated only if session is greater than this value (0 = always, 24*60*60 = every 24 hours)
...userOptions.session
...userOptions.session,
},
// JWT options
jwt: {
@@ -119,20 +140,20 @@ async function NextAuthHandler (req, res, userOptions) {
maxAge, // same as session maxAge,
encode: jwt.encode,
decode: jwt.decode,
...userOptions.jwt
...userOptions.jwt,
},
// Event messages
events: {
...defaultEvents,
...userOptions.events
...userOptions.events,
},
// Callback functions
callbacks: {
...defaultCallbacks,
...userOptions.callbacks
...userOptions.callbacks,
},
pkce: {},
logger
logger,
}
csrfTokenHandler(req, res)
@@ -141,64 +162,74 @@ async function NextAuthHandler (req, res, userOptions) {
const render = renderPage(req, res)
const { pages } = req.options
if (req.method === 'GET') {
if (req.method === "GET") {
switch (action) {
case 'providers':
case "providers":
return routes.providers(req, res)
case 'session':
case "session":
return routes.session(req, res)
case 'csrf':
case "csrf":
return res.json({ csrfToken: req.options.csrfToken })
case 'signin':
case "signin":
if (pages.signIn) {
let signinUrl = `${pages.signIn}${pages.signIn.includes('?') ? '&' : '?'}callbackUrl=${req.options.callbackUrl}`
if (error) { signinUrl = `${signinUrl}&error=${error}` }
let signinUrl = `${pages.signIn}${
pages.signIn.includes("?") ? "&" : "?"
}callbackUrl=${req.options.callbackUrl}`
if (error) {
signinUrl = `${signinUrl}&error=${error}`
}
return res.redirect(signinUrl)
}
return render.signin()
case 'signout':
case "signout":
if (pages.signOut) return res.redirect(pages.signOut)
return render.signout()
case 'callback':
case "callback":
if (provider) {
if (await pkce.handleCallback(req, res)) return
if (await state.handleCallback(req, res)) return
return routes.callback(req, res)
}
break
case 'verify-request':
case "verify-request":
if (pages.verifyRequest) {
return res.redirect(pages.verifyRequest)
}
return render.verifyRequest()
case 'error':
case "error":
if (pages.error) {
return res.redirect(`${pages.error}${pages.error.includes('?') ? '&' : '?'}error=${error}`)
return res.redirect(
`${pages.error}${
pages.error.includes("?") ? "&" : "?"
}error=${error}`
)
}
// These error messages are displayed in line on the sign in page
if ([
'Signin',
'OAuthSignin',
'OAuthCallback',
'OAuthCreateAccount',
'EmailCreateAccount',
'Callback',
'OAuthAccountNotLinked',
'EmailSignin',
'CredentialsSignin'
].includes(error)) {
if (
[
"Signin",
"OAuthSignin",
"OAuthCallback",
"OAuthCreateAccount",
"EmailCreateAccount",
"Callback",
"OAuthAccountNotLinked",
"EmailSignin",
"CredentialsSignin",
].includes(error)
) {
return res.redirect(`${baseUrl}${basePath}/signin?error=${error}`)
}
return render.error({ error })
default:
}
} else if (req.method === 'POST') {
} else if (req.method === "POST") {
switch (action) {
case 'signin':
case "signin":
// Verified CSRF Token required for all sign in routes
if (req.options.csrfTokenVerified && provider) {
if (await pkce.handleSignin(req, res)) return
@@ -207,16 +238,19 @@ async function NextAuthHandler (req, res, userOptions) {
}
return res.redirect(`${baseUrl}${basePath}/signin?csrf=true`)
case 'signout':
case "signout":
// Verified CSRF Token required for signout
if (req.options.csrfTokenVerified) {
return routes.signout(req, res)
}
return res.redirect(`${baseUrl}${basePath}/signout?csrf=true`)
case 'callback':
case "callback":
if (provider) {
// Verified CSRF Token required for credentials providers only
if (provider.type === 'credentials' && !req.options.csrfTokenVerified) {
if (
provider.type === "credentials" &&
!req.options.csrfTokenVerified
) {
return res.redirect(`${baseUrl}${basePath}/signin?csrf=true`)
}
@@ -225,31 +259,33 @@ async function NextAuthHandler (req, res, userOptions) {
return routes.callback(req, res)
}
break
case '_log':
case "_log":
if (userOptions.logger) {
try {
const {
code = 'CLIENT_ERROR',
level = 'error',
message = '[]'
code = "CLIENT_ERROR",
level = "error",
message = "[]",
} = req.body
logger[level](code, ...JSON.parse(message))
} catch (error) {
// If logging itself failed...
logger.error('LOGGER_ERROR', error)
logger.error("LOGGER_ERROR", error)
}
}
return res.end()
default:
}
}
return res.status(400).end(`Error: HTTP ${req.method} is not supported for ${req.url}`)
return res
.status(400)
.end(`Error: HTTP ${req.method} is not supported for ${req.url}`)
})
}
/** Tha main entry point to next-auth */
export default function NextAuth (...args) {
export default function NextAuth(...args) {
if (args.length === 1) {
return (req, res) => NextAuthHandler(req, res, args[0])
}

31
types/adapters.d.ts vendored
View File

@@ -1,13 +1,36 @@
import { AppOptions } from "./internals"
import { User, Profile, Session } from "."
import { EmailConfig } from "./providers"
import { ConnectionOptions } from "typeorm"
/** Legacy */
export {
TypeORMAccountModel,
TypeORMSessionModel,
TypeORMUserModel,
TypeORMVerificationRequestModel,
} from "@next-auth/typeorm-legacy-adapter"
import {
TypeORMAdapter,
TypeORMAdapterModels,
} from "@next-auth/typeorm-legacy-adapter"
import { PrismaLegacyAdapter } from "@next-auth/prisma-legacy-adapter"
export const TypeORM: {
Models: TypeORMAdapterModels
Adapter: TypeORMAdapter
}
export const Prisma: {
Adapter: PrismaLegacyAdapter
}
declare const Adapters: {
Default: Adapter<ConnectionOptions>
TypeORM: { Adapter: Adapter<ConnectionOptions> }
Prisma: { Adapter: Adapter }
Default: TypeORMAdapter
TypeORM: typeof TypeORM
Prisma: typeof Prisma
}
export default Adapters

View File

@@ -94,6 +94,7 @@ export type OAuthProviderType =
| "Twitter"
| "VK"
| "WordPress"
| "WorkOS"
| "Yandex"
| "Zoho"

View File

@@ -156,9 +156,9 @@ Check out the content of all the params in addition `token`, to see what info yo
:::
:::warning
NextAuth.js does not limit how much data you can store in a JSON Web Token, however a ~**4096 byte limit** for all cookies on a domain is commonly imposed by browsers.
NextAuth.js does not limit how much data you can store in a JSON Web Token, however a ~**4096 byte limit** per cookie is commonly imposed by browsers.
If you need to persist a large amount of data, you will need to persist it elsewhere (e.g. in a database). You can store a key that can be used to look up that data in the `session()` callback.
If you need to persist a large amount of data, you will need to persist it elsewhere (e.g. in a database). A common solution is to store a key in the cookie that can be used to look up the remaining data in the database, for example, in the `session()` callback.
:::
## Session callback

View File

@@ -196,9 +196,9 @@ JSON Web Tokens can be used for session tokens, but are also used for lots of ot
NextAuth.js client includes advanced features to mitigate the downsides of using shorter session expiry times on the user experience, including automatic session token rotation, optionally sending keep alive messages to prevent short lived sessions from expiring if there is an window or tab open, background re-validation, and automatic tab/window syncing that keeps sessions in sync across windows any time session state changes or a window or tab gains or loses focus.
* As with database session tokens, JSON Web Tokens are limited in the amount of data you can store in them. There is typically a limit of around 4096 bytes in total for all cookies on a domain, though the exact limit varies between browsers, proxies and hosting services.
* As with database session tokens, JSON Web Tokens are limited in the amount of data you can store in them. There is typically a limit of around 4096 bytes per cookie, though the exact limit varies between browsers, proxies and hosting services. If you want to support most browsers, then do not exceed 4096 bytes per cookie. If you want to save more data, you will need to persist your sessions in a database (Source: [browsercookielimits.iain.guru](http://browsercookielimits.iain.guru/))
The more data you try to store in a token and the more other cookies you set, the closer you will come to this limit. If you wish to store more than ~2 KB of data you probably at the point where you need to store a unique ID in the token and persist the data elsewhere (e.g. in a server side key/value store).
The more data you try to store in a token and the more other cookies you set, the closer you will come to this limit. If you wish to store more than ~4 KB of data you're probably at the point where you need to store a unique ID in the token and persist the data elsewhere (e.g. in a server-side key/value store).
* Data stored in an encrypted JSON Web Token (JWE) may be compromised at some point.

View File

@@ -11,6 +11,14 @@ https://api.intra.42.fr/apidoc/guides/web_application_flow
https://profile.intra.42.fr/oauth/applications/new
## Options
The **42 School Provider** comes with a set of default options:
- [42 School Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/42.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -11,6 +11,14 @@ https://developer.apple.com/sign-in-with-apple/get-started/
https://developer.apple.com/account/resources/identifiers/list/serviceId
## Options
The **Apple Provider** comes with a set of default options:
- [Apple Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/apple.js)
You can override any of the options to suit your own use case.
## Example
There are two ways you can use the Sign in with Apple provider.
@@ -25,7 +33,7 @@ import Providers from `next-auth/providers`
providers: [
Providers.Apple({
clientId: process.env.APPLE_ID,
clientSecret: {
clientSecret: {
teamId: process.env.APPLE_TEAM_ID,
privateKey: process.env.APPLE_PRIVATE_KEY,
keyId: process.env.APPLE_KEY_ID,
@@ -40,18 +48,18 @@ providers: [
You can convert your Apple key to a single line to use it in a environment variable.
**Mac**
```bash
awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' AuthKey_ID.k8
```
**Windows**
```powershell
$k8file = "AuthKey_ID.k8"
(Get-Content "C:\Users\$env:UserName\Downloads\${k8file}") -join "\n"
(Get-Content "C:\Users\$env:UserName\Downloads\${k8file}") -join "\n"
```
:::
### Pre-generated secret
@@ -92,9 +100,9 @@ Apple doesn't allow you to use localhost in domains or subdomains.
The following guides may be helpful:
* [How to setup localhost with HTTPS with a Next.js app](https://medium.com/@anMagpie/secure-your-local-development-server-with-https-next-js-81ac6b8b3d68)
- [How to setup localhost with HTTPS with a Next.js app](https://medium.com/@anMagpie/secure-your-local-development-server-with-https-next-js-81ac6b8b3d68)
* [Guide to configuring Sign in with Apple](https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple)
- [Guide to configuring Sign in with Apple](https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple)
### Example server
@@ -114,7 +122,6 @@ Add-Content -Path C:\Windows\System32\drivers\etc\hosts -Value "127.0.0.1`tdev.e
#### Create certificate
Creating a certificate for localhost is easy with openssl . Just put the following command in the terminal. The output will be two files: localhost.key and localhost.crt.
```bash
@@ -127,7 +134,7 @@ openssl req -x509 -out localhost.crt -keyout localhost.key \
:::tip
**Windows**
The OpenSSL executable is distributed with [Git](https://git-scm.com/download/win]9) for Windows.
The OpenSSL executable is distributed with [Git](https://git-scm.com/download/win]9) for Windows.
Once installed you will find the openssl.exe file in `C:/Program Files/Git/mingw64/bin` which you can add to the system PATH environment variable if its not already done.
Add environment variable `OPENSSL_CONF=C:/Program Files/Git/mingw64/ssl/openssl.cnf`
@@ -142,32 +149,30 @@ Add environment variable `OPENSSL_CONF=C:/Program Files/Git/mingw64/ssl/openssl.
Create directory `certificates` and place `localhost.key` and `localhost.crt`
You can create a `server.js` in the root of your project and run it with `node server.js` to test Sign in with Apple integration locally:
```js
const { createServer } = require('https')
const { parse } = require('url')
const next = require('next')
const fs = require('fs')
const { createServer } = require("https")
const { parse } = require("url")
const next = require("next")
const fs = require("fs")
const dev = process.env.NODE_ENV !== 'production'
const dev = process.env.NODE_ENV !== "production"
const app = next({ dev })
const handle = app.getRequestHandler()
const httpsOptions = {
key: fs.readFileSync('./certificates/localhost.key'),
cert: fs.readFileSync('./certificates/localhost.crt')
key: fs.readFileSync("./certificates/localhost.key"),
cert: fs.readFileSync("./certificates/localhost.crt"),
}
app.prepare().then(() => {
createServer(httpsOptions, (req, res) => {
const parsedUrl = parse(req.url, true)
handle(req, res, parsedUrl)
}).listen(3000, err => {
}).listen(3000, (err) => {
if (err) throw err
console.log('> Ready on https://localhost:3000')
console.log("> Ready on https://localhost:3000")
})
})
```
@@ -177,25 +182,28 @@ app.prepare().then(() => {
If you want to pre-generate your secret, this is an example of the code you will need:
```js
const jwt = require('jsonwebtoken')
const fs = require('fs')
const jwt = require("jsonwebtoken")
const fs = require("fs")
const appleId = 'myapp.example.com'
const keyId = ''
const teamId = ''
const privateKey = fs.readFileSync('path/to/key')
const appleId = "myapp.example.com"
const keyId = ""
const teamId = ""
const privateKey = fs.readFileSync("path/to/key")
const secret = jwt.sign(
{
iss: teamId,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + ( 86400 * 180 ), // 6 months
aud: 'https://appleid.apple.com',
sub: appleId
}, privateKey, {
algorithm: 'ES256',
keyid: keyId
})
exp: Math.floor(Date.now() / 1000) + 86400 * 180, // 6 months
aud: "https://appleid.apple.com",
sub: appleId,
},
privateKey,
{
algorithm: "ES256",
keyid: keyId,
}
)
console.log(secret)
```

View File

@@ -7,6 +7,14 @@ title: Atlassian
https://developer.atlassian.com/cloud/jira/platform/oauth-2-authorization-code-grants-3lo-for-apps/#implementing-oauth-2-0--3lo-
## Options
The **Atlassian Provider** comes with a set of default options:
- [Atlassian Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/atlassian.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -15,6 +15,14 @@ https://manage.auth0.com/dashboard
Configure your application in Auth0 as a 'Regular Web Application' (not a 'Single Page App').
:::
## Options
The **Auth0 Provider** comes with a set of default options:
- [Auth0 Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/auth0.js)
You can override any of the options to suit your own use case.
## Example
```js
@@ -32,4 +40,4 @@ providers: [
:::note
`domain` should be the fully qualified domain  e.g. `dev-s6clz2lv.eu.auth0.com`
:::
:::

View File

@@ -11,7 +11,16 @@ https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-c
https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant
## Options
The **Azure Active Directory Provider** comes with a set of default options:
- [Azure Active Directory Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/azure-ad-b2c.js)
You can override any of the options to suit your own use case.
## Example
- In https://portal.azure.com/ -> Azure Active Directory create a new App Registration.
- Make sure to remember / copy
- Application (client) ID
@@ -22,13 +31,13 @@ https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tena
In `.env.local` create the follwing entries:
```
AZURE_CLIENT_ID=<copy Application (client) ID here>
AZURE_CLIENT_ID=<copy Application (client) ID here>
AZURE_CLIENT_SECRET=<copy generated secret value here>
AZURE_TENANT_ID=<copy the tenant id here>
```
In `pages/api/auth/[...nextauth].js` find or add the AZURE entries:
```js
import Providers from 'next-auth/providers';
...

View File

@@ -11,9 +11,18 @@ https://github.com/basecamp/api/blob/master/sections/authentication.md
https://launchpad.37signals.com/integrations
## Options
The **Basecamp Provider** comes with a set of default options:
- [Basecamp Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/basecamp.js)
You can override any of the options to suit your own use case.
## Examples
### Basic profile information
```js
import Providers from `next-auth/providers`
...
@@ -27,7 +36,7 @@ providers: [
```
:::note
Using the example above, it is only possible to retrieve profile information such as account id, email and name. If you wish to retrieve user data in relation to a specific team, you must provide a different profileUrl and a custom function to handle profile information as shown in the example below.
Using the example above, it is only possible to retrieve profile information such as account id, email and name. If you wish to retrieve user data in relation to a specific team, you must provide a different profileUrl and a custom function to handle profile information as shown in the example below.
:::
### Profile information in relation to specific team
@@ -57,4 +66,4 @@ providers: [
:::tip
The BASECAMP_TEAM_ID is found in the url path of your team's homepage. For example, if the url is `https://3.basecamp.com/1234567/projects`, then in this case the BASECAMP_TEAM_ID is 1234567
:::
:::

View File

@@ -11,6 +11,14 @@ https://develop.battle.net/documentation/guides/using-oauth
https://develop.battle.net/access/clients
## Options
The **Battle.net Provider** comes with a set of default options:
- [Battle.net Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/battlenet.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -11,6 +11,14 @@ https://developer.box.com/reference/
https://developer.box.com/guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-box/
## Options
The **Box Provider** comes with a set of default options:
- [Box Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/box.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -11,6 +11,14 @@ https://github.com/Bungie-net/api/wiki/OAuth-Documentation
https://www.bungie.net/en/Application
## Options
The **Bungie Provider** comes with a set of default options:
- [Bungie Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/bungie.js)
You can override any of the options to suit your own use case.
## Example
```js
@@ -28,8 +36,6 @@ providers: [
...
```
## Instructions
### Configuration
:::tip
@@ -42,20 +48,20 @@ Bungie doesn't allow you to use localhost as the website URL, instead you need t
Navigate to https://www.bungie.net/en/Application and fill in the required details:
* Application name
* Application Status
* Website
* OAuth Client Type
- Application name
- Application Status
- Website
- OAuth Client Type
- Confidential
* Redirect URL
- Redirect URL
- https://localhost:3000/api/auth/callback/bungie
* Scope
- Scope
- `Access items like your Bungie.net notifications, memberships, and recent Bungie.Net forum activity.`
* Origin Header
- Origin Header
The following guide may be helpful:
* [How to setup localhost with HTTPS with a Next.js app](https://medium.com/@anMagpie/secure-your-local-development-server-with-https-next-js-81ac6b8b3d68)
- [How to setup localhost with HTTPS with a Next.js app](https://medium.com/@anMagpie/secure-your-local-development-server-with-https-next-js-81ac6b8b3d68)
### Example server
@@ -75,7 +81,6 @@ Add-Content -Path C:\Windows\System32\drivers\etc\hosts -Value "127.0.0.1`tdev.e
#### Create certificate
Creating a certificate for localhost is easy with openssl. Just put the following command in the terminal. The output will be two files: localhost.key and localhost.crt.
```bash
@@ -103,32 +108,30 @@ Add environment variable `OPENSSL_CONF=C:/Program Files/Git/mingw64/ssl/openssl.
Create directory `certificates` and place `localhost.key` and `localhost.crt`
You can create a `server.js` in the root of your project and run it with `node server.js` to test Sign in with Bungie integration locally:
```js
const { createServer } = require('https')
const { parse } = require('url')
const next = require('next')
const fs = require('fs')
const { createServer } = require("https")
const { parse } = require("url")
const next = require("next")
const fs = require("fs")
const dev = process.env.NODE_ENV !== 'production'
const dev = process.env.NODE_ENV !== "production"
const app = next({ dev })
const handle = app.getRequestHandler()
const httpsOptions = {
key: fs.readFileSync('./certificates/localhost.key'),
cert: fs.readFileSync('./certificates/localhost.crt')
key: fs.readFileSync("./certificates/localhost.key"),
cert: fs.readFileSync("./certificates/localhost.crt"),
}
app.prepare().then(() => {
createServer(httpsOptions, (req, res) => {
const parsedUrl = parse(req.url, true)
handle(req, res, parsedUrl)
}).listen(3000, err => {
}).listen(3000, (err) => {
if (err) throw err
console.log('> Ready on https://localhost:3000')
console.log("> Ready on https://localhost:3000")
})
})
```

View File

@@ -13,6 +13,14 @@ https://console.aws.amazon.com/cognito/users/
You need to select your AWS region to go the the Cognito dashboard.
## Options
The **Amazon Cognito Provider** comes with a set of default options:
- [Amazon Cognito Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/cognito.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -15,6 +15,14 @@ It comes with the constraint that users authenticated in this manner are not per
The functionality provided for credentials based authentication is intentionally limited to discourage use of passwords due to the inherent security risks associated with them and the additional complexity associated with supporting usernames and passwords.
:::
## Options
The **Credentials Provider** comes with a set of default options:
- [Credentials Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/credentials.js)
You can override any of the options to suit your own use case.
## Example
The Credentials provider is specified like other providers, except that you need to define a handler for `authorize()` that accepts credentials submitted via HTTP POST as input and returns either:

View File

@@ -11,6 +11,14 @@ https://discord.com/developers/docs/topics/oauth2
https://discord.com/developers/applications
## Options
The **Discord Provider** comes with a set of default options:
- [Discord Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/discord.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -11,6 +11,14 @@ https://developers.dropbox.com/oauth-guide
https://www.dropbox.com/developers/apps
## Options
The **Dropbox Provider** comes with a set of default options:
- [Dropbox Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/dropbox.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -15,70 +15,80 @@ The Email provider can be used in conjunction with (or instead of) one or more O
On initial sign in, a **Verification Token** is sent to the email address provided. By default this token is valid for 24 hours. If the Verification Token is used with that time (i.e. by clicking on the link in the email) an account is created for the user and they are signed in.
If someone provides the email address of an *existing account* when signing in, an email is sent and they are signed into the account associated with that email address when they follow the link in the email.
If someone provides the email address of an _existing account_ when signing in, an email is sent and they are signed into the account associated with that email address when they follow the link in the email.
:::tip
The Email Provider can be used with both JSON Web Tokens and database sessions, but you **must** configure a database to use it. It is not possible to enable email sign in without using a database.
:::
## Options
The **Email Provider** comes with a set of default options:
- [Email Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/email.js)
You can override any of the options to suit your own use case.
## Configuration
1. You will need an SMTP account; ideally for one of the [services known to work with nodemailer](http://nodemailer.com/smtp/well-known/).
2. There are two ways to configure the SMTP server connection.
You can either use a connection string or a nodemailer configuration object.
You can either use a connection string or a nodemailer configuration object.
2.1 **Using a connection string**
2.1 **Using a connection string**
Create an .env file to the root of your project and add the connection string and email address.
```js title=".env" {1}
Create an .env file to the root of your project and add the connection string and email address.
```js title=".env" {1}
EMAIL_SERVER=smtp://username:password@smtp.example.com:587
EMAIL_FROM=noreply@example.com
```
```
Now you can add the email provider like this:
Now you can add the email provider like this:
```js {3} title="pages/api/auth/[...nextauth].js"
providers: [
Providers.Email({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM
}),
],
```
```js {3} title="pages/api/auth/[...nextauth].js"
providers: [
Providers.Email({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM
}),
],
```
2.2 **Using a configuration object**
2.2 **Using a configuration object**
In your `.env` file in the root of your project simply add the configuration object options individually:
In your `.env` file in the root of your project simply add the configuration object options individually:
```js title=".env"
EMAIL_SERVER_USER=username
EMAIL_SERVER_PASSWORD=password
EMAIL_SERVER_HOST=smtp.example.com
```js title=".env"
EMAIL_SERVER_USER=username
EMAIL_SERVER_PASSWORD=password
EMAIL_SERVER_HOST=smtp.example.com
EMAIL_SERVER_PORT=587
EMAIL_FROM=noreply@example.com
```
Now you can add the provider settings to the NextAuth options object in the Email Provider.
```
Now you can add the provider settings to the NextAuth options object in the Email Provider.
```js title="pages/api/auth/[...nextauth].js"
providers: [
Providers.Email({
server: {
host: process.env.EMAIL_SERVER_HOST,
port: process.env.EMAIL_SERVER_PORT,
auth: {
user: process.env.EMAIL_SERVER_USER,
pass: process.env.EMAIL_SERVER_PASSWORD
}
},
from: process.env.EMAIL_FROM
}),
],
```
```js title="pages/api/auth/[...nextauth].js"
providers: [
Providers.Email({
server: {
host: process.env.EMAIL_SERVER_HOST,
port: process.env.EMAIL_SERVER_PORT,
auth: {
user: process.env.EMAIL_SERVER_USER,
pass: process.env.EMAIL_SERVER_PASSWORD
}
},
from: process.env.EMAIL_FROM
}),
],
```
3. You can now sign in with an email address at `/api/auth/signin`.
An account will not be created for the user until the first time they verify their email address. If an email address already associated with an account, the user will be signed in to that account when they use the link in the email.
A user account (i.e. an entry in the Users table) will not be created for the user until the first time they verify their email address. If an email address is already associated with an account, the user will be signed in to that account when they use the link in the email.
## Customising emails
@@ -89,39 +99,54 @@ e.g.
```js {3} title="pages/api/auth/[...nextauth].js"
providers: [
Providers.Email({
server: process.env.EMAIL_SERVER,
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
sendVerificationRequest: ({ identifier: email, url, token, baseUrl, provider }) => { /* your function */ }
})
sendVerificationRequest: ({
identifier: email,
url,
token,
baseUrl,
provider,
}) => {
/* your function */
},
}),
]
```
The following code shows the complete source for the built-in `sendVerificationRequest()` method:
```js
import nodemailer from 'nodemailer'
import nodemailer from "nodemailer"
const sendVerificationRequest = ({ identifier: email, url, token, baseUrl, provider }) => {
const sendVerificationRequest = ({
identifier: email,
url,
token,
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?:\/\//, '')
const site = baseUrl.replace(/^https?:\/\//, "")
nodemailer
.createTransport(server)
.sendMail({
nodemailer.createTransport(server).sendMail(
{
to: email,
from,
subject: `Sign in to ${site}`,
text: text({ url, site, email }),
html: html({ url, site, email })
}, (error) => {
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))
logger.error("SEND_VERIFICATION_EMAIL_ERROR", email, error)
return reject(new Error("SEND_VERIFICATION_EMAIL_ERROR", error))
}
return resolve()
})
}
)
})
}
@@ -131,16 +156,16 @@ const html = ({ url, site, email }) => {
// email address and the domain from being turned into a hyperlink by email
// clients like Outlook and Apple mail, as this is confusing because it seems
// like they are supposed to click on their email address to sign in.
const escapedEmail = `${email.replace(/\./g, '&#8203;.')}`
const escapedSite = `${site.replace(/\./g, '&#8203;.')}`
const escapedEmail = `${email.replace(/\./g, "&#8203;.")}`
const escapedSite = `${site.replace(/\./g, "&#8203;.")}`
// Some simple styling options
const backgroundColor = '#f9f9f9'
const textColor = '#444444'
const mainBackgroundColor = '#ffffff'
const buttonBackgroundColor = '#346df1'
const buttonBorderColor = '#346df1'
const buttonTextColor = '#ffffff'
const backgroundColor = "#f9f9f9"
const textColor = "#444444"
const mainBackgroundColor = "#ffffff"
const buttonBackgroundColor = "#346df1"
const buttonBorderColor = "#346df1"
const buttonTextColor = "#ffffff"
// Uses tables for layout and inline CSS due to email client limitations
return `
@@ -185,7 +210,6 @@ const text = ({ url, site }) => `Sign in to ${site}\n${url}\n\n`
If you want to generate great looking email client compatible HTML with React, check out https://mjml.io
:::
## Customising the Verification Token
By default, we are generating a random verification token. You can define a `generateVerificationToken` method in your provider options if you want to override it:
@@ -197,4 +221,5 @@ providers: [
return "ABC123"
}
})
],
],
```

View File

@@ -11,6 +11,14 @@ https://developers.eveonline.com/blog/article/sso-to-authenticated-calls
https://developers.eveonline.com/
## Options
The **EVE Online Provider** comes with a set of default options:
- [EVE Online Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/eveonline.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -11,6 +11,14 @@ https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/
https://developers.facebook.com/apps/
## Options
The **Facebook Provider** comes with a set of default options:
- [Facebook Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/facebook.js)
You can override any of the options to suit your own use case.
## Example
```js
@@ -31,4 +39,4 @@ Production applications cannot use localhost URLs to sign in with Facebook. You
:::tip
Email address may not be returned for accounts created on mobile.
:::
:::

View File

@@ -15,6 +15,14 @@ Grant type: `Authorization Code`
Scopes to have basic infos (email, nickname, guid and avatar) : `openid`, `email`, `profile`
## Options
The **FACEIT Provider** comes with a set of default options:
- [FACEIT Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/faceit.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -14,6 +14,14 @@ https://developer.foursquare.com/
:::warning
Foursquare requires an additional `apiVersion` parameter in [`YYYYMMDD` format](https://developer.foursquare.com/docs/places-api/versioning/), which essentially states "I'm prepared for API changes up to this date".
## Options
The **Foursquare Provider** comes with a set of default options:
- [Foursquare Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/foursquare.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -7,6 +7,14 @@ title: FusionAuth
https://fusionauth.io/docs/v1/tech/oauth/
## Options
The **FusionAuth Provider** comes with a set of default options:
- [FusionAuth Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/fusionauth.js)
You can override any of the options to suit your own use case.
## Example
```js
@@ -14,8 +22,8 @@ import Providers from `next-auth/providers`
...
providers: [
Providers.FusionAuth({
id: "fusionauth",
name: "FusionAuth",
id: "fusionauth",
name: "FusionAuth",
domain: process.env.FUSIONAUTH_DOMAIN,
clientId: process.env.FUSIONAUTH_CLIENT_ID,
clientSecret: process.env.FUSIONAUTH_SECRET,
@@ -40,7 +48,8 @@ For more information, follow the [FusionAuth 5-minute setup guide](https://fusio
:::
In the OAuth settings for your application, configure the following.
* Redirect URL
- Redirect URL
- https://localhost:3000/api/auth/callback/fusionauth
* Enabled grants
- Make sure *Authorization Code* is enabled.
- Enabled grants
- Make sure _Authorization Code_ is enabled.

View File

@@ -11,6 +11,14 @@ https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps
https://github.com/settings/apps
## Options
The **Github Provider** comes with a set of default options:
- [Github Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/github.js)
You can override any of the options to suit your own use case.
## Example
```js
@@ -30,5 +38,5 @@ Only allows one callback URL per Client ID / Client Secret.
:::
:::tip
Email address is not returned if privacy settings are enabled.
:::
Email address is not returned if privacy settings are enabled.
:::

View File

@@ -11,6 +11,14 @@ https://docs.gitlab.com/ee/api/oauth2.html
https://gitlab.com/profile/applications
## Options
The **Gitlab Provider** comes with a set of default options:
- [Gitlab Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/gitlab.js)
You can override any of the options to suit your own use case.
## Example
```js
@@ -26,5 +34,5 @@ providers: [
```
:::tip
Enable the *"read_user"* option in scope if you want to save the users email address on sign up.
:::
Enable the _"read_user"_ option in scope if you want to save the users email address on sign up.
:::

View File

@@ -11,6 +11,14 @@ https://developers.google.com/identity/protocols/oauth2
https://console.developers.google.com/apis/credentials
## Options
The **Google Provider** comes with a set of default options:
- [Google Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/google.js)
You can override any of the options to suit your own use case.
## Example
```js
@@ -48,6 +56,7 @@ const options = {
...
}
```
:::
:::tip
@@ -72,4 +81,5 @@ const options = {
...
}
```
:::

View File

@@ -7,6 +7,14 @@ title: IdentityServer4
https://identityserver4.readthedocs.io/en/latest/
## Options
The **IdentityServer4 Provider** comes with a set of default options:
- [IdentityServer4 Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/identity-server4.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -11,6 +11,14 @@ https://developers.facebook.com/docs/instagram-basic-display-api/getting-started
https://developers.facebook.com/apps/
## Options
The **Instagram Provider** comes with a set of default options:
- [Instagram Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/instagram.js)
You can override any of the options to suit your own use case.
## Example
```jsx
@@ -39,4 +47,4 @@ Email address is not returned by the Instagram API.
:::tip
Instagram display app required callback URL to be configured in your Facebook app and Facebook required you to use **https** even for localhost! In order to do that, you either need to [add an SSL to your localhost](https://www.freecodecamp.org/news/how-to-get-https-working-on-your-local-development-environment-in-5-minutes-7af615770eec/) or use a proxy such as [ngrock](https://ngrok.com/docs).
:::
:::

View File

@@ -11,6 +11,14 @@ https://developers.kakao.com/product/kakaoLogin
https://developers.kakao.com/docs/latest/en/kakaologin/common
## Options
The **Kakao Provider** comes with a set of default options:
- [Kakao Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/kakao.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -11,6 +11,14 @@ https://developers.line.biz/en/docs/line-login/integrate-line-login/
https://developers.line.biz/console/
## Options
The **Line Provider** comes with a set of default options:
- [Line Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/line.js)
You can override any of the options to suit your own use case.
## Example
```js
@@ -32,4 +40,4 @@ providers: [
Create a provider and a LINE login channel at `https://developers.line.biz/console/`. In the settings of the channel under LINE Login, activate web app and configure the following:
- Callback URL
- http://localhost:3000/api/auth/callback/line
- http://localhost:3000/api/auth/callback/line

View File

@@ -15,6 +15,14 @@ From the Auth tab get the client ID and client secret. On the same tab, add redi
![image](https://user-images.githubusercontent.com/330396/114429603-68195600-9b72-11eb-8311-62e58383c42b.png)
## Options
The **LinkedIn Provider** comes with a set of default options:
- [LinkedIn Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/linked-in.js)
You can override any of the options to suit your own use case.
## Example
```js
@@ -27,3 +35,4 @@ providers: [
})
]
...
```

View File

@@ -11,6 +11,14 @@ https://mailchimp.com/developer/marketing/guides/access-user-data-oauth-2/
https://admin.mailchimp.com/account/oauth2/client/
## Options
The **Mailchimp Provider** comes with a set of default options:
- [Mailchimp Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/mailchimp.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -11,6 +11,14 @@ https://o2.mail.ru/docs
https://o2.mail.ru/app/
## Options
The **Mail.ru Provider** comes with a set of default options:
- [Mail.ru Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/mailru.js)
You can override any of the options to suit your own use case.
## Example
```js
@@ -23,3 +31,4 @@ providers: [
})
]
...
```

View File

@@ -11,6 +11,14 @@ https://github.com/Medium/medium-api-docs
https://medium.com/me/applications
## Options
The **Medium Provider** comes with a set of default options:
- [Medium Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/medium.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -11,6 +11,14 @@ https://www.netlify.com/blog/2016/10/10/integrating-with-netlify-oauth2/
https://github.com/netlify/netlify-oauth-example
## Options
The **Netlify Provider** comes with a set of default options:
- [Netlify Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/netlify.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -7,6 +7,14 @@ title: Okta
https://developer.okta.com/docs/reference/api/oidc
## Options
The **Okta Provider** comes with a set of default options:
- [Okta Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/okta.js)
You can override any of the options to suit your own use case.
## Example
```js
@@ -20,4 +28,4 @@ providers: [
})
]
...
```
```

View File

@@ -17,6 +17,14 @@ You can configure your OAuth Clients on your Osso Admin UI, i.e. https://demo.os
See Osso's complete configuration and testing documentation at https://ossoapp.com/docs/configure/overview
## Options
The **Osso Provider** comes with a set of default options:
- [Osso Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/osso.js)
You can override any of the options to suit your own use case.
## Example
A full example application is available at https://github.com/enterprise-oss/osso-next-auth-example and https://nextjs-demo.ossoapp.com

View File

@@ -11,6 +11,14 @@ https://www.reddit.com/dev/api/
https://www.reddit.com/prefs/apps/
## Options
The **Reddit Provider** comes with a set of default options:
- [Reddit Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/reddit.js)
You can override any of the options to suit your own use case.
## Example
```js
@@ -38,27 +46,28 @@ This Provider template only has a one hour access token to it and only has the '
```js
providers: [
{
id: "reddit",
name: "Reddit",
clientId: process.env.REDDIT_CLIENT_ID,
clientSecret: process.env.REDDIT_CLIENT_SECRET,
scope: "identity mysubreddits read", //Check Reddit API Documentation for more. The identity scope is required.
type: "oauth",
version: "2.0",
params: { grant_type: "authorization_code" },
accessTokenUrl: " https://www.reddit.com/api/v1/access_token",
authorizationUrl:
"https://www.reddit.com/api/v1/authorize?response_type=code&duration=permanent",
profileUrl: "https://oauth.reddit.com/api/v1/me",
profile: (profile) => {
return {
id: profile.id,
name: profile.name,
email: null,
}
{
id: "reddit",
name: "Reddit",
clientId: process.env.REDDIT_CLIENT_ID,
clientSecret: process.env.REDDIT_CLIENT_SECRET,
scope: "identity mysubreddits read", //Check Reddit API Documentation for more. The identity scope is required.
type: "oauth",
version: "2.0",
params: { grant_type: "authorization_code" },
accessTokenUrl: " https://www.reddit.com/api/v1/access_token",
authorizationUrl:
"https://www.reddit.com/api/v1/authorize?response_type=code&duration=permanent",
profileUrl: "https://oauth.reddit.com/api/v1/me",
profile: (profile) => {
return {
id: profile.id,
name: profile.name,
email: null,
}
}
]
},
},
]
```
:::

View File

@@ -7,6 +7,14 @@ title: Salesforce
https://help.salesforce.com/articleView?id=remoteaccess_authenticate.htm&type=5
## Options
The **Salesforce Provider** comes with a set of default options:
- [Salesforce Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/salesforce.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -12,6 +12,14 @@ https://api.slack.com/docs/sign-in-with-slack
https://api.slack.com/apps
## Options
The **Slack Provider** comes with a set of default options:
- [Slack Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/slack.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -11,6 +11,14 @@ https://developer.spotify.com/documentation
https://developer.spotify.com/dashboard/applications
## Options
The **Spotify Provider** comes with a set of default options:
- [Spotify Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/spotify.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

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

View File

@@ -13,6 +13,14 @@ https://dev.twitch.tv/console/apps
Add the following redirect URL into the console `http://<your-next-app-url>/api/auth/callback/twitch`
## Options
The **Twitch Provider** comes with a set of default options:
- [Twitch Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/twitch.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -11,6 +11,14 @@ https://developer.twitter.com
https://developer.twitter.com/en/apps
## Options
The **Twitter Provider** comes with a set of default options:
- [Twitter Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/twitter.js)
You can override any of the options to suit your own use case.
## Example
```js
@@ -26,7 +34,7 @@ providers: [
```
:::tip
You must enable the *"Request email address from users"* option in your app permissions if you want to obtain the users email address.
You must enable the _"Request email address from users"_ option in your app permissions if you want to obtain the users email address.
:::
![twitter](https://user-images.githubusercontent.com/7902980/83944068-1640ca80-a801-11ea-959c-0e744e2144f7.PNG)
![twitter](https://user-images.githubusercontent.com/7902980/83944068-1640ca80-a801-11ea-959c-0e744e2144f7.PNG)

View File

@@ -11,6 +11,14 @@ https://vk.com/dev/first_guide
https://vk.com/apps?act=manage
## Options
The **VK Provider** comes with a set of default options:
- [VK Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/vk.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -11,6 +11,14 @@ https://developer.wordpress.com/docs/oauth2/
https://developer.wordpress.com/apps/
## Options
The **Wordpress Provider** comes with a set of default options:
- [Wordpress Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/wordpress.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -0,0 +1,31 @@
---
id: workos
title: WorkOS
---
## Documentation
https://workos.com/docs/sso/guide
## Options
The **WorkOS Provider** comes with a set of default options:
- [WorkOS Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/workos.js)
You can override any of the options to suit your own use case.
## Example
```js
import Providers from `next-auth/providers`
...
providers: [
Providers.WorkOS({
clientId: process.env.WORKOS_ID,
clientSecret: process.env.WORKOS_SECRET,
domain: process.env.WORKOS_DOMAIN
}),
],
...
```

View File

@@ -11,6 +11,14 @@ https://tech.yandex.com/oauth/doc/dg/concepts/about-docpage/
https://oauth.yandex.com/client/new
## Options
The **Yandex Provider** comes with a set of default options:
- [Yandex Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/yandex.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -11,6 +11,14 @@ https://www.zoho.com/accounts/protocol/oauth/web-server-applications.html
https://api-console.zoho.com/
## Options
The **Zoho Provider** comes with a set of default options:
- [Zoho Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/zoho.js)
You can override any of the options to suit your own use case.
## Example
```js

View File

@@ -48,7 +48,7 @@ export default {
```
:::note
[View source for built-in TypeORM models and schemas](https://github.com/nextauthjs/next-auth/tree/main/src/adapters/typeorm/models)
[View source for built-in TypeORM models and schemas](https://github.com/nextauthjs/adapters/tree/canary/packages/typeorm-legacy/src/models)
:::
## Using custom models

24167
www/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,23 +4,26 @@
"scripts": {
"start": "npm run generate-providers && docusaurus start",
"build": "npm run generate-providers && docusaurus build",
"docusaurus": "docusaurus",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"serve": "docusaurus serve",
"clear": "docusaurus clear",
"lint": "standard",
"lint:fix": "standard --fix",
"generate-providers": "node ./scripts/generate-providers.js"
},
"dependencies": {
"@docusaurus/core": "^2.0.0-alpha.70",
"@docusaurus/preset-classic": "^2.0.0-alpha.70",
"classnames": "^2.2.6",
"docusaurus-lunr-search": "^2.1.10",
"jose": "^2.0.2",
"@docusaurus/core": "2.0.0-beta.0",
"@docusaurus/preset-classic": "2.0.0-beta.0",
"classnames": "^2.3.1",
"docusaurus-lunr-search": "^2.1.14",
"jose": "^2.0.5",
"lodash.times": "^4.3.2",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-marquee-slider": "^1.1.2",
"styled-components": "^5.2.1"
"styled-components": "^5.2.3"
},
"browserslist": {
"production": [

View File

@@ -34,9 +34,9 @@
html[data-theme="dark"]:root {
--ifm-color-link: #289ef9;
--ifm-footer-background-color: #111;
--ifm-footer-background-color: #000;
--ifm-html-background-color: #242526;
--ifm-background-color: #000000;
--ifm-background-color: #090909;
--ifm-hero-background-color: #111111;
--ifm-navbar-background-color: rgba(0, 0, 0, 0.95);
}
@@ -198,6 +198,21 @@ html[data-theme="dark"] hr {
background-repeat: no-repeat;
}
.navbar__items .react-toggle {
margin-right: 10px;
}
.navbar__search-input:focus {
outline: none;
box-shadow: rgb(255, 255, 255) 0px 0px 0px 0px,
rgba(19, 19, 19, 0.2) 0px 0px 0px 4px, rgba(0, 0, 0, 0) 0px 0px 0px 0px;
transition: box-shadow 350ms ease-in-out;
}
html[data-theme="dark"] .navbar__search-input:focus {
box-shadow: rgb(255, 255, 255) 0px 0px 0px 0px,
rgba(29, 29, 29, 0.5) 0px 0px 0px 4px, rgba(0, 0, 0, 0) 0px 0px 0px 0px;
}
html[data-theme="dark"] .navbar__item.navbar__link[href*="github"]:before {
background-image: url("/img/brand-github-inverted.svg");
}

View File

@@ -148,7 +148,7 @@
margin-right: -4px;
height: 100%;
vertical-align: middle;
content: '';
content: "";
}
.searchbox__submit:hover,
@@ -237,7 +237,7 @@
.algolia-autocomplete .ds-dropdown-menu:before {
display: block;
position: absolute;
content: '';
content: "";
width: 14px;
height: 14px;
background: #373940;
@@ -308,7 +308,7 @@
cursor: pointer;
}
.algolia-autocomplete .ds-dropdown-menu [class^='ds-dataset-'] {
.algolia-autocomplete .ds-dropdown-menu [class^="ds-dataset-"] {
position: relative;
border-radius: 4px;
overflow: auto;
@@ -369,7 +369,7 @@
}
.algolia-autocomplete .algolia-docsearch-suggestion--content:before {
content: '';
content: "";
position: absolute;
display: block;
top: 0;
@@ -413,7 +413,7 @@
}
.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before {
content: '';
content: "";
position: absolute;
display: block;
top: 0;
@@ -484,7 +484,7 @@
color: #222222;
background-color: #ebebeb;
border-radius: 3px;
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}

View File

@@ -6,17 +6,19 @@
* LICENSE file in the root directory of this source tree.
*/
import React, { useRef, useCallback } from "react";
import classnames from "classnames";
import { useHistory } from "@docusaurus/router";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
let loaded = false;
import React, { useRef, useCallback, useEffect } from "react"
import classnames from "classnames"
import { useHistory } from "@docusaurus/router"
import useDocusaurusContext from "@docusaurus/useDocusaurusContext"
import "./styles.css"
let loaded = false
const Search = (props) => {
const initialized = useRef(false);
const searchBarRef = useRef(null);
const history = useHistory();
const { siteConfig = {} } = useDocusaurusContext();
const { baseUrl } = siteConfig;
const initialized = useRef(false)
const searchBarRef = useRef(null)
const history = useHistory()
const { siteConfig = {} } = useDocusaurusContext()
const { baseUrl } = siteConfig
const initAlgolia = () => {
if (!initialized.current) {
new window.DocSearch({
@@ -25,25 +27,25 @@ const Search = (props) => {
// Override algolia's default selection event, allowing us to do client-side
// navigation and avoiding a full page refresh.
handleSelected: (_input, _event, suggestion) => {
const url = baseUrl + suggestion.url;
const url = baseUrl + suggestion.url
// Use an anchor tag to parse the absolute url into a relative url
// Alternatively, we can use new URL(suggestion.url) but its not supported in IE
const a = document.createElement("a");
a.href = url;
const a = document.createElement("a")
a.href = url
// Algolia use closest parent element id #__docusaurus when a h1 page title does not have an id
// So, we can safely remove it. See https://github.com/facebook/docusaurus/issues/1828 for more details.
history.push(url);
history.push(url)
},
});
initialized.current = true;
})
initialized.current = true
}
};
}
const getSearchData = () =>
process.env.NODE_ENV === "production"
? fetch(`${baseUrl}search-doc.json`).then((content) => content.json())
: Promise.resolve([]);
: Promise.resolve([])
const loadAlgolia = () => {
if (!loaded) {
@@ -52,26 +54,38 @@ const Search = (props) => {
import("./lib/DocSearch"),
import("./algolia.css"),
]).then(([searchData, { default: DocSearch }]) => {
loaded = true;
window.searchData = searchData;
window.DocSearch = DocSearch;
initAlgolia();
});
loaded = true
window.searchData = searchData
window.DocSearch = DocSearch
initAlgolia()
})
} else {
initAlgolia();
initAlgolia()
}
};
}
const toggleSearchIconClick = useCallback(
(e) => {
if (!searchBarRef.current.contains(e.target)) {
searchBarRef.current.focus();
searchBarRef.current.focus()
}
props.handleSearchBarToggle(!props.isSearchBarExpanded);
props.handleSearchBarToggle &&
props.handleSearchBarToggle(!props.isSearchBarExpanded)
},
[props.isSearchBarExpanded]
);
)
useEffect(() => {
document.addEventListener("keypress", (e) => {
if (document.activeElement === searchBarRef.current) return
if (e.key === "/") {
e.preventDefault()
searchBarRef.current.focus()
}
})
return () => document.removeEventListener("keypress")
}, [])
return (
<div className="navbar__search" key="search-box">
@@ -101,8 +115,27 @@ const Search = (props) => {
onBlur={toggleSearchIconClick}
ref={searchBarRef}
/>
</div>
);
};
export default Search;
<svg
x="0px"
y="0px"
width="19px"
height="20px"
viewBox="0 0 19 20"
className={classnames("search-icon-keyboard", {
"search-icon-hidden": props.isSearchBarExpanded,
})}
>
<path
fill="none"
stroke="#979A9C"
opacity="0.4"
d="M3.5,0.5h12c1.7,0,3,1.3,3,3v13c0,1.7-1.3,3-3,3h-12c-1.7,0-3-1.3-3-3v-13C0.5,1.8,1.8,0.5,3.5,0.5z"
/>
<path fill="#979a9c" d="M11.8,6L8,15.1H7.1L10.8,6L11.8,6z" />
</svg>
</div>
)
}
export default Search

View File

@@ -1,9 +1,9 @@
import Hogan from 'hogan.js'
import LunrSearchAdapter from './lunar-search'
import autocomplete from 'autocomplete.js'
import templates from './templates'
import utils from './utils'
import $ from './zepto'
import Hogan from "hogan.js"
import LunrSearchAdapter from "./lunar-search"
import autocomplete from "autocomplete.js"
import templates from "./templates"
import utils from "./utils"
import $ from "./zepto"
/**
* Adds an autocomplete dropdown to an input field
@@ -22,7 +22,7 @@ const usage = `Usage:
[ autocompleteOptions.{hint,debug} ]
})`
class DocSearch {
constructor ({
constructor({
searchData,
inputSelector,
debug = false,
@@ -30,40 +30,40 @@ class DocSearch {
autocompleteOptions = {
debug: false,
hint: false,
autoselect: true
autoselect: true,
},
transformData = false,
queryHook = false,
handleSelected = false,
enhancedSearchInput = false,
layout = 'collumns'
layout = "collumns",
}) {
DocSearch.checkArguments({
searchData,
inputSelector
inputSelector,
})
this.searchData = searchData
this.input = DocSearch.getInputFromSelector(inputSelector)
this.queryDataCallback = queryDataCallback || null
const autocompleteOptionsDebug =
autocompleteOptions && autocompleteOptions.debug
? autocompleteOptions.debug
: false
autocompleteOptions && autocompleteOptions.debug
? autocompleteOptions.debug
: false
// eslint-disable-next-line no-param-reassign
autocompleteOptions.debug = debug || autocompleteOptionsDebug
this.autocompleteOptions = autocompleteOptions
this.autocompleteOptions.cssClasses =
this.autocompleteOptions.cssClasses || {}
this.autocompleteOptions.cssClasses || {}
this.autocompleteOptions.cssClasses.prefix =
this.autocompleteOptions.cssClasses.prefix || 'ds'
this.autocompleteOptions.cssClasses.prefix || "ds"
const inputAriaLabel =
this.input &&
typeof this.input.attr === 'function' &&
this.input.attr('aria-label')
this.input &&
typeof this.input.attr === "function" &&
this.input.attr("aria-label")
this.autocompleteOptions.ariaLabel =
this.autocompleteOptions.ariaLabel || inputAriaLabel || 'search input'
this.autocompleteOptions.ariaLabel || inputAriaLabel || "search input"
this.isSimpleLayout = layout === 'simple'
this.isSimpleLayout = layout === "simple"
this.client = new LunrSearchAdapter(this.searchData)
@@ -76,9 +76,9 @@ class DocSearch {
templates: {
suggestion: DocSearch.getSuggestionTemplate(this.isSimpleLayout),
footer: templates.footer,
empty: DocSearch.getEmptyTemplate()
}
}
empty: DocSearch.getEmptyTemplate(),
},
},
])
const customHandleSelected = handleSelected
@@ -86,18 +86,18 @@ class DocSearch {
// We prevent default link clicking if a custom handleSelected is defined
if (customHandleSelected) {
$('.algolia-autocomplete').on('click', '.ds-suggestions a', event => {
$(".algolia-autocomplete").on("click", ".ds-suggestions a", (event) => {
event.preventDefault()
})
}
this.autocomplete.on(
'autocomplete:selected',
"autocomplete:selected",
this.handleSelected.bind(null, this.autocomplete.autocomplete)
)
this.autocomplete.on(
'autocomplete:shown',
"autocomplete:shown",
this.handleShown.bind(null, this.input)
)
@@ -107,87 +107,84 @@ class DocSearch {
}
/**
* Checks that the passed arguments are valid. Will throw errors otherwise
* @function checkArguments
* @param {object} args Arguments as an option object
* @returns {void}
*/
static checkArguments (args) {
* Checks that the passed arguments are valid. Will throw errors otherwise
* @function checkArguments
* @param {object} args Arguments as an option object
* @returns {void}
*/
static checkArguments(args) {
if (!args.searchData) {
throw new Error(usage)
}
if (typeof args.inputSelector !== 'string') {
if (typeof args.inputSelector !== "string") {
throw new Error(
`Error: inputSelector:${args.inputSelector} must be a string. Each selector must match only one element and separated by ','`
`Error: inputSelector:${args.inputSelector} must be a string. Each selector must match only one element and separated by ','`
)
}
if (!DocSearch.getInputFromSelector(args.inputSelector)) {
throw new Error(
`Error: No input element in the page matches ${args.inputSelector}`
`Error: No input element in the page matches ${args.inputSelector}`
)
}
}
static injectSearchBox (input) {
static injectSearchBox(input) {
input.before(templates.searchBox)
const newInput = input
.prev()
.prev()
.find('input')
const newInput = input.prev().prev().find("input")
input.remove()
return newInput
}
static bindSearchBoxEvent () {
$('.searchbox [type="reset"]').on('click', function () {
$('input#docsearch').focus()
$(this).addClass('hide')
autocomplete.autocomplete.setVal('')
static bindSearchBoxEvent() {
$('.searchbox [type="reset"]').on("click", function () {
$("input#docsearch").focus()
$(this).addClass("hide")
autocomplete.autocomplete.setVal("")
})
$('input#docsearch').on('keyup', () => {
const searchbox = document.querySelector('input#docsearch')
$("input#docsearch").on("keyup", () => {
const searchbox = document.querySelector("input#docsearch")
const reset = document.querySelector('.searchbox [type="reset"]')
reset.className = 'searchbox__reset'
reset.className = "searchbox__reset hide"
if (searchbox.value.length === 0) {
reset.className += ' hide'
reset.className += " hide"
}
})
}
/**
* Returns the matching input from a CSS selector, null if none matches
* @function getInputFromSelector
* @param {string} selector CSS selector that matches the search
* input of the page
* @returns {void}
*/
static getInputFromSelector (selector) {
const input = $(selector).filter('input')
* Returns the matching input from a CSS selector, null if none matches
* @function getInputFromSelector
* @param {string} selector CSS selector that matches the search
* input of the page
* @returns {void}
*/
static getInputFromSelector(selector) {
const input = $(selector).filter("input")
return input.length ? $(input[0]) : null
}
/**
* Returns the `source` method to be passed to autocomplete.js. It will query
* the Algolia index and call the callbacks with the formatted hits.
* @function getAutocompleteSource
* @param {function} transformData An optional function to transform the hits
* @param {function} queryHook An optional function to transform the query
* @returns {function} Method to be passed as the `source` option of
* autocomplete
*/
getAutocompleteSource (transformData, queryHook) {
* Returns the `source` method to be passed to autocomplete.js. It will query
* the Algolia index and call the callbacks with the formatted hits.
* @function getAutocompleteSource
* @param {function} transformData An optional function to transform the hits
* @param {function} queryHook An optional function to transform the query
* @returns {function} Method to be passed as the `source` option of
* autocomplete
*/
getAutocompleteSource(transformData, queryHook) {
return (query, callback) => {
if (queryHook) {
// eslint-disable-next-line no-param-reassign
query = queryHook(query) || query
}
this.client.search(query).then(hits => {
this.client.search(query).then((hits) => {
if (
this.queryDataCallback &&
typeof this.queryDataCallback === 'function'
typeof this.queryDataCallback === "function"
) {
this.queryDataCallback(hits)
}
@@ -201,58 +198,57 @@ class DocSearch {
// Given a list of hits returned by the API, will reformat them to be used in
// a Hogan template
static formatHits (receivedHits) {
static formatHits(receivedHits) {
const clonedHits = utils.deepClone(receivedHits)
const hits = clonedHits.map(hit => {
const hits = clonedHits.map((hit) => {
if (hit._highlightResult) {
// eslint-disable-next-line no-param-reassign
hit._highlightResult = utils.mergeKeyWithParent(
hit._highlightResult,
'hierarchy'
"hierarchy"
)
}
return utils.mergeKeyWithParent(hit, 'hierarchy')
return utils.mergeKeyWithParent(hit, "hierarchy")
})
// Group hits by category / subcategory
let groupedHits = utils.groupBy(hits, 'lvl0')
let groupedHits = utils.groupBy(hits, "lvl0")
$.each(groupedHits, (level, collection) => {
const groupedHitsByLvl1 = utils.groupBy(collection, 'lvl1')
const groupedHitsByLvl1 = utils.groupBy(collection, "lvl1")
const flattenedHits = utils.flattenAndFlagFirst(
groupedHitsByLvl1,
'isSubCategoryHeader'
"isSubCategoryHeader"
)
groupedHits[level] = flattenedHits
})
groupedHits = utils.flattenAndFlagFirst(groupedHits, 'isCategoryHeader')
groupedHits = utils.flattenAndFlagFirst(groupedHits, "isCategoryHeader")
// Translate hits into smaller objects to be send to the template
return groupedHits.map(hit => {
return groupedHits.map((hit) => {
const url = DocSearch.formatURL(hit)
const category = utils.getHighlightedValue(hit, 'lvl0')
const subcategory = utils.getHighlightedValue(hit, 'lvl1') || category
const category = utils.getHighlightedValue(hit, "lvl0")
const subcategory = utils.getHighlightedValue(hit, "lvl1") || category
const displayTitle = utils
.compact([
utils.getHighlightedValue(hit, 'lvl2') || subcategory,
utils.getHighlightedValue(hit, 'lvl3'),
utils.getHighlightedValue(hit, 'lvl4'),
utils.getHighlightedValue(hit, 'lvl5'),
utils.getHighlightedValue(hit, 'lvl6')
utils.getHighlightedValue(hit, "lvl2") || subcategory,
utils.getHighlightedValue(hit, "lvl3"),
utils.getHighlightedValue(hit, "lvl4"),
utils.getHighlightedValue(hit, "lvl5"),
utils.getHighlightedValue(hit, "lvl6"),
])
.join(
'<span class="aa-suggestion-title-separator" aria-hidden="true"> </span>'
)
const text = utils.getSnippetedValue(hit, 'content')
const text = utils.getSnippetedValue(hit, "content")
const isTextOrSubcategoryNonEmpty =
(subcategory && subcategory !== '') ||
(displayTitle && displayTitle !== '')
(subcategory && subcategory !== "") ||
(displayTitle && displayTitle !== "")
const isLvl1EmptyOrDuplicate =
!subcategory || subcategory === '' || subcategory === category
!subcategory || subcategory === "" || subcategory === category
const isLvl2 =
displayTitle && displayTitle !== '' && displayTitle !== subcategory
displayTitle && displayTitle !== "" && displayTitle !== subcategory
const isLvl1 =
!isLvl2 &&
(subcategory && subcategory !== '' && subcategory !== category)
!isLvl2 && subcategory && subcategory !== "" && subcategory !== category
const isLvl0 = !isLvl1 && !isLvl2
return {
@@ -267,50 +263,50 @@ class DocSearch {
subcategory,
title: displayTitle,
text,
url
url,
}
})
}
static formatURL (hit) {
static formatURL(hit) {
const { url, anchor } = hit
if (url) {
const containsAnchor = url.indexOf('#') !== -1
const containsAnchor = url.indexOf("#") !== -1
if (containsAnchor) return url
else if (anchor) return `${hit.url}#${hit.anchor}`
return url
} else if (anchor) return `#${hit.anchor}`
/* eslint-disable */
console.warn("no anchor nor url for : ", JSON.stringify(hit));
/* eslint-enable */
console.warn("no anchor nor url for : ", JSON.stringify(hit))
/* eslint-enable */
return null
}
static getEmptyTemplate () {
return args => Hogan.compile(templates.empty).render(args)
static getEmptyTemplate() {
return (args) => Hogan.compile(templates.empty).render(args)
}
static getSuggestionTemplate (isSimpleLayout) {
static getSuggestionTemplate(isSimpleLayout) {
const stringTemplate = isSimpleLayout
? templates.suggestionSimple
: templates.suggestion
const template = Hogan.compile(stringTemplate)
return suggestion => template.render(suggestion)
return (suggestion) => template.render(suggestion)
}
handleSelected (input, event, suggestion, datasetNumber, context = {}) {
handleSelected(input, event, suggestion, datasetNumber, context = {}) {
// Do nothing if click on the suggestion, as it's already a <a href>, the
// browser will take care of it. This allow Ctrl-Clicking on results and not
// having the main window being redirected as well
if (context.selectionMethod === 'click') {
if (context.selectionMethod === "click") {
return
}
input.setVal('')
input.setVal("")
window.location.assign(suggestion.url)
}
handleShown (input) {
handleShown(input) {
const middleOfInput = input.offset().left + input.width() / 2
let middleOfWindow = $(document).width() / 2
@@ -319,14 +315,14 @@ class DocSearch {
}
const alignClass =
middleOfInput - middleOfWindow >= 0
? 'algolia-autocomplete-right'
: 'algolia-autocomplete-left'
middleOfInput - middleOfWindow >= 0
? "algolia-autocomplete-right"
: "algolia-autocomplete-left"
const otherAlignClass =
middleOfInput - middleOfWindow < 0
? 'algolia-autocomplete-right'
: 'algolia-autocomplete-left'
const autocompleteWrapper = $('.algolia-autocomplete')
middleOfInput - middleOfWindow < 0
? "algolia-autocomplete-right"
: "algolia-autocomplete-left"
const autocompleteWrapper = $(".algolia-autocomplete")
if (!autocompleteWrapper.hasClass(alignClass)) {
autocompleteWrapper.addClass(alignClass)
}

View File

@@ -1,4 +1,4 @@
const prefix = 'algolia-docsearch'
const prefix = "algolia-docsearch"
const suggestionPrefix = `${prefix}-suggestion`
const footerPrefix = `${prefix}-footer`
@@ -89,26 +89,22 @@ const templates = {
<form novalidate="novalidate" onsubmit="return false;" class="searchbox">
<div role="search" class="searchbox__wrapper">
<input id="docsearch" type="search" name="search" placeholder="Search the docs" autocomplete="off" required="required" class="searchbox__input"/>
<button type="submit" title="Submit your search query." class="searchbox__submit" >
<svg width=12 height=12 role="img" aria-label="Search">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-search-13"></use>
</svg>
</button>
<button type="reset" title="Clear the search query." class="searchbox__reset hide">
<svg width=12 height=12 role="img" aria-label="Reset">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-clear-3"></use>
</svg>
</button>
</div>
</form>
<div class="svg-icons" style="height: 0; width: 0; position: absolute; visibility: hidden">
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="sbx-icon-clear-3" viewBox="0 0 40 40"><path d="M16.228 20L1.886 5.657 0 3.772 3.772 0l1.885 1.886L20 16.228 34.343 1.886 36.228 0 40 3.772l-1.886 1.885L23.772 20l14.342 14.343L40 36.228 36.228 40l-1.885-1.886L20 23.772 5.657 38.114 3.772 40 0 36.228l1.886-1.885L16.228 20z" fill-rule="evenodd"></symbol>
<symbol id="sbx-icon-clear-3" viewBox="0 0 40 40"></symbol>
<symbol id="sbx-icon-search-13" viewBox="0 0 40 40"><path d="M26.806 29.012a16.312 16.312 0 0 1-10.427 3.746C7.332 32.758 0 25.425 0 16.378 0 7.334 7.333 0 16.38 0c9.045 0 16.378 7.333 16.378 16.38 0 3.96-1.406 7.593-3.746 10.426L39.547 37.34c.607.608.61 1.59-.004 2.203a1.56 1.56 0 0 1-2.202.004L26.807 29.012zm-10.427.627c7.322 0 13.26-5.938 13.26-13.26 0-7.324-5.938-13.26-13.26-13.26-7.324 0-13.26 5.936-13.26 13.26 0 7.322 5.936 13.26 13.26 13.26z" fill-rule="evenodd"></symbol>
</svg>
</div>
`
`,
}
export default templates

View File

@@ -17,10 +17,22 @@
display: none;
}
.search-icon-keyboard {
position: absolute;
right: 30px;
top: 20px;
}
.search-icon-hidden {
visibility: hidden;
}
.searchbox__input::-webkit-search-decoration,
.searchbox__input::-webkit-search-cancel-button {
visibility: none !important;
display: none !important;
}
@media (max-width: 360px) {
.search-bar {
width: 0 !important;
@@ -37,4 +49,13 @@
display: inline;
vertical-align: sub;
}
.search-icon-keyboard {
display: none;
}
}
input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
background: none;
}