mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
32 Commits
v3.8.0
...
v3.14.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1e30507c2 | ||
|
|
2c4fce3699 | ||
|
|
8fa71512d1 | ||
|
|
d420eeff9d | ||
|
|
0d863d38bc | ||
|
|
6f9f42a85b | ||
|
|
2160be2a8a | ||
|
|
55eb066793 | ||
|
|
5bc8f8b986 | ||
|
|
136361e1f4 | ||
|
|
cc9869592c | ||
|
|
073da60c3d | ||
|
|
aacc34bbfd | ||
|
|
074688d10e | ||
|
|
b3ffe50c03 | ||
|
|
e6d063825d | ||
|
|
985f7b3431 | ||
|
|
237b016378 | ||
|
|
776b9480da | ||
|
|
07a3f76cb3 | ||
|
|
3726d68c49 | ||
|
|
e31db1726a | ||
|
|
a241199c11 | ||
|
|
5385ec20a9 | ||
|
|
810d02e671 | ||
|
|
e5535734f8 | ||
|
|
ba7aed1057 | ||
|
|
a7e08e2a32 | ||
|
|
0d13040264 | ||
|
|
582520f8ef | ||
|
|
95942519a5 | ||
|
|
f3e64f04cc |
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -9,7 +9,7 @@ assignees: ''
|
||||
A clear and concise description of the feature being proposed.
|
||||
|
||||
**Purpose of proposed feature**
|
||||
A clear and concise description description of why this feature is necessary and what problems it solves.
|
||||
A clear and concise description of why this feature is necessary and what problems it solves.
|
||||
|
||||
**Detail about proposed feature**
|
||||
A detailed description of how the proposal might work (if you have one).
|
||||
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -6,10 +6,12 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- beta
|
||||
- next
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- beta
|
||||
- next
|
||||
|
||||
jobs:
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -13,7 +13,7 @@ name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, next ]
|
||||
branches: [ main, beta, next ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ main ]
|
||||
|
||||
11
.github/workflows/integration.yml
vendored
11
.github/workflows/integration.yml
vendored
@@ -2,9 +2,10 @@ name: Integration Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- next
|
||||
branches:
|
||||
- main
|
||||
- beta
|
||||
- next
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
@@ -17,7 +18,7 @@ jobs:
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
|
||||
# We use self-hosted runners as cloud based runnners (e.g. AWS, GPC)
|
||||
# fail due to IP Address checks done by providers, which enforce
|
||||
# fail due to IP Address checks done by providers, which enforce
|
||||
# CAPTCHA checks on login request from cloud compute IP addresses to
|
||||
# prevent abuse.
|
||||
runs-on: self-hosted
|
||||
@@ -45,7 +46,7 @@ jobs:
|
||||
- run: npm test
|
||||
# TODO Tests should exit out if env vars not set (currently hangs)
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||
NEXTAUTH_TWITTER_ID: ${{secrets.NEXTAUTH_TWITTER_ID}}
|
||||
NEXTAUTH_TWITTER_SECRET: ${{secrets.NEXTAUTH_TWITTER_SECRET}}
|
||||
NEXTAUTH_TWITTER_USERNAME: ${{secrets.NEXTAUTH_TWITTER_USERNAME}}
|
||||
|
||||
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@@ -3,6 +3,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'beta'
|
||||
- 'next'
|
||||
- '3.x'
|
||||
pull_request:
|
||||
|
||||
25
.github/workflows/types.yml
vendored
Normal file
25
.github/workflows/types.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Types
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- beta
|
||||
- next
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- beta
|
||||
- next
|
||||
|
||||
jobs:
|
||||
lint-and-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- name: Install dependencies
|
||||
uses: bahmutov/npm-install@v1
|
||||
- name: Check types
|
||||
run: npm run test:types
|
||||
3
.npmignore
Normal file
3
.npmignore
Normal file
@@ -0,0 +1,3 @@
|
||||
./types/tests/
|
||||
./types/tests/tsconfig.json
|
||||
./types/tests/tslint.json
|
||||
3
.prettierrc
Normal file
3
.prettierrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"semi": false
|
||||
}
|
||||
23
config/build-types.js
Normal file
23
config/build-types.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const BUILD_TARGETS = [
|
||||
'index.d.ts',
|
||||
'client.d.ts',
|
||||
'adapters.d.ts',
|
||||
'providers.d.ts',
|
||||
'jwt.d.ts',
|
||||
'_next.d.ts',
|
||||
'_utils.d.ts'
|
||||
]
|
||||
|
||||
BUILD_TARGETS.forEach((target) => {
|
||||
fs.copyFile(
|
||||
path.resolve('types', target),
|
||||
path.join(process.cwd(), target),
|
||||
(err) => {
|
||||
if (err) throw err
|
||||
console.log(`[build-types] copying "${target}" to root folder`)
|
||||
}
|
||||
)
|
||||
})
|
||||
26903
package-lock.json
generated
26903
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
@@ -7,9 +7,10 @@
|
||||
"author": "Iain Collins <me@iaincollins.com>",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "npm run build:js && npm run build:css",
|
||||
"build": "npm run build:js && npm run build:css && npm run build:types",
|
||||
"build:js": "babel --config-file ./config/babel.config.json src --out-dir dist",
|
||||
"build:css": "postcss --config config/postcss.config.js src/**/*.css --base src --dir dist && node config/wrap-css.js",
|
||||
"build:types": "node ./config/build-types.js",
|
||||
"dev": "next | npm run watch:css",
|
||||
"watch": "npm run watch:js | npm run watch:css",
|
||||
"watch:js": "babel --config-file ./config/babel.config.json --watch src --out-dir dist",
|
||||
@@ -17,13 +18,14 @@
|
||||
"test:app:start": "docker-compose -f test/docker/app.yml up -d",
|
||||
"test:app:rebuild": "npm run build && docker-compose -f test/docker/app.yml up -d --build",
|
||||
"test:app:stop": "docker-compose -f test/docker/app.yml down",
|
||||
"test": "npm run test:app:rebuild && npm run test:integration && npm run test:app:stop",
|
||||
"test": "npm run test:app:rebuild && npm run test:integration && npm run test:app:stop && npm run test:types",
|
||||
"test:db": "npm run test:db:mysql && npm run test:db:postgres && npm run test:db:mongodb && npm run test:db:mssql",
|
||||
"test:db:mysql": "node test/mysql.js",
|
||||
"test:db:postgres": "node test/postgres.js",
|
||||
"test:db:mongodb": "node test/mongodb.js",
|
||||
"test:db:mssql": "node test/mssql.js",
|
||||
"test:integration": "mocha test/integration",
|
||||
"test:types": "dtslint types",
|
||||
"db:start": "docker-compose -f test/docker/databases.yml up -d",
|
||||
"db:stop": "docker-compose -f test/docker/databases.yml down",
|
||||
"prepublishOnly": "npm run build",
|
||||
@@ -35,10 +37,17 @@
|
||||
"files": [
|
||||
"dist",
|
||||
"index.js",
|
||||
"index.d.ts",
|
||||
"providers.js",
|
||||
"providers.d.ts",
|
||||
"adapters.js",
|
||||
"adapters.d.ts",
|
||||
"client.js",
|
||||
"jwt.js"
|
||||
"client.d.ts",
|
||||
"jwt.js",
|
||||
"jwt.d.ts",
|
||||
"_next.d.ts",
|
||||
"_utils.d.ts"
|
||||
],
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
@@ -50,14 +59,14 @@
|
||||
"oauth": "^0.9.15",
|
||||
"pkce-challenge": "^2.1.0",
|
||||
"preact": "^10.4.1",
|
||||
"preact-render-to-string": "^5.1.7",
|
||||
"preact-render-to-string": "^5.1.14",
|
||||
"querystring": "^0.2.0",
|
||||
"require_optional": "^1.0.1",
|
||||
"typeorm": "^0.2.30"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17",
|
||||
"react-dom": "^16.13.1 || ^17"
|
||||
"react-dom": "16.13.1 || ^17"
|
||||
},
|
||||
"peerOptionalDependencies": {
|
||||
"mongodb": "^3.5.9",
|
||||
@@ -81,6 +90,7 @@
|
||||
"conventional-changelog-conventionalcommits": "4.4.0",
|
||||
"cssnano": "^4.1.10",
|
||||
"dotenv": "^8.2.0",
|
||||
"dtslint": "^4.0.8",
|
||||
"eslint": "^7.19.0",
|
||||
"mocha": "^8.1.3",
|
||||
"mongodb": "^3.5.9",
|
||||
@@ -90,6 +100,7 @@
|
||||
"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",
|
||||
@@ -103,7 +114,8 @@
|
||||
"project": "./tsconfig.json",
|
||||
"ignore": [
|
||||
"test/",
|
||||
"next-env.d.ts"
|
||||
"next-env.d.ts",
|
||||
"types/"
|
||||
],
|
||||
"globals": [
|
||||
"localStorage",
|
||||
|
||||
@@ -2,6 +2,7 @@ module.exports = {
|
||||
branches: [
|
||||
'+([0-9])?(.{+([0-9]),x}).x',
|
||||
'main',
|
||||
{ name: 'beta', prerelease: true },
|
||||
{ name: 'next', prerelease: true }
|
||||
]
|
||||
}
|
||||
|
||||
2
src/client/index.d.ts
vendored
2
src/client/index.d.ts
vendored
@@ -29,6 +29,8 @@ type GetSession<S extends Record<string, unknown> = DefaultSession> = (options:
|
||||
export interface NextAuthConfig {
|
||||
baseUrl: string
|
||||
basePath: string
|
||||
baseUrlServer: string
|
||||
basePathServer: string
|
||||
/** 0 means disabled (don't send); 60 means send every 60 seconds */
|
||||
keepAlive: number
|
||||
/** 0 means disabled (only use cache); 60 means sync if last checked > 60 seconds ago */
|
||||
|
||||
@@ -22,6 +22,8 @@ import parseUrl from '../lib/parse-url'
|
||||
const __NEXTAUTH = {
|
||||
baseUrl: parseUrl(process.env.NEXTAUTH_URL || process.env.VERCEL_URL).baseUrl,
|
||||
basePath: parseUrl(process.env.NEXTAUTH_URL).basePath,
|
||||
baseUrlServer: parseUrl(process.env.NEXTAUTH_URL_INTERNAL || process.env.NEXTAUTH_URL || process.env.VERCEL_URL).baseUrl,
|
||||
basePathServer: parseUrl(process.env.NEXTAUTH_URL_INTERNAL || process.env.NEXTAUTH_URL).basePath,
|
||||
keepAlive: 0,
|
||||
clientMaxAge: 0,
|
||||
// Properties starting with _ are used for tracking internal app state
|
||||
@@ -69,7 +71,12 @@ const SessionContext = createContext()
|
||||
*/
|
||||
export function useSession (session) {
|
||||
const context = useContext(SessionContext)
|
||||
const [data, setData] = useState(context?.[0] ?? session)
|
||||
if (context) return context
|
||||
return _useSessionHook(session)
|
||||
}
|
||||
|
||||
function _useSessionHook (session) {
|
||||
const [data, setData] = useState(session)
|
||||
const [loading, setLoading] = useState(!data)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -123,9 +130,9 @@ export function useSession (session) {
|
||||
__NEXTAUTH._clientSession = newClientSessionData
|
||||
|
||||
setData(newClientSessionData)
|
||||
setLoading(false)
|
||||
} catch (error) {
|
||||
logger.error('CLIENT_USE_SESSION_ERROR', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
@@ -359,7 +366,7 @@ function _apiBaseUrl () {
|
||||
}
|
||||
|
||||
// Return absolute path when called server side
|
||||
return `${__NEXTAUTH.baseUrl}${__NEXTAUTH.basePath}`
|
||||
return `${__NEXTAUTH.baseUrlServer}${__NEXTAUTH.basePathServer}`
|
||||
}
|
||||
// Return relative path when called client side
|
||||
return __NEXTAUTH.basePath
|
||||
|
||||
25
src/providers/faceit.js
Normal file
25
src/providers/faceit.js
Normal file
@@ -0,0 +1,25 @@
|
||||
export default (options) => {
|
||||
return {
|
||||
id: 'faceit',
|
||||
name: 'FACEIT',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
headers: {
|
||||
Authorization: `Basic ${Buffer.from(`${options.clientId}:${options.clientSecret}`).toString('base64')}`
|
||||
},
|
||||
accessTokenUrl: 'https://api.faceit.com/auth/v1/oauth/token',
|
||||
authorizationUrl: 'https://accounts.faceit.com/accounts?redirect_popup=true&response_type=code',
|
||||
profileUrl: 'https://api.faceit.com/auth/v1/resources/userinfo',
|
||||
profile (profile) {
|
||||
const { guid: id, nickname: name, email, picture: image } = profile
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
email,
|
||||
image
|
||||
}
|
||||
},
|
||||
...options
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import Discord from './discord'
|
||||
import Email from './email'
|
||||
import EVEOnline from './eveonline'
|
||||
import Facebook from './facebook'
|
||||
import FACEIT from './faceit'
|
||||
import Foursquare from './foursquare'
|
||||
import FusionAuth from './fusionauth'
|
||||
import GitHub from './github'
|
||||
@@ -19,12 +20,14 @@ import GitLab from './gitlab'
|
||||
import Google from './google'
|
||||
import IdentityServer4 from './identity-server4'
|
||||
import Instagram from './instagram'
|
||||
import Kakao from './kakao'
|
||||
import LINE from './line'
|
||||
import LinkedIn from './linkedin'
|
||||
import MailRu from './mailru'
|
||||
import Medium from './medium'
|
||||
import Netlify from './netlify'
|
||||
import Okta from './okta'
|
||||
import Osso from './osso'
|
||||
import Reddit from './reddit'
|
||||
import Salesforce from './salesforce'
|
||||
import Slack from './slack'
|
||||
@@ -34,6 +37,7 @@ import Twitch from './twitch'
|
||||
import Twitter from './twitter'
|
||||
import VK from './vk'
|
||||
import Yandex from './yandex'
|
||||
import Zoho from './zoho'
|
||||
|
||||
export default {
|
||||
Apple,
|
||||
@@ -50,6 +54,7 @@ export default {
|
||||
Email,
|
||||
EVEOnline,
|
||||
Facebook,
|
||||
FACEIT,
|
||||
Foursquare,
|
||||
FusionAuth,
|
||||
GitHub,
|
||||
@@ -57,12 +62,14 @@ export default {
|
||||
Google,
|
||||
IdentityServer4,
|
||||
Instagram,
|
||||
Kakao,
|
||||
LINE,
|
||||
LinkedIn,
|
||||
MailRu,
|
||||
Medium,
|
||||
Netlify,
|
||||
Okta,
|
||||
Osso,
|
||||
Reddit,
|
||||
Salesforce,
|
||||
Slack,
|
||||
@@ -71,5 +78,6 @@ export default {
|
||||
Twitch,
|
||||
Twitter,
|
||||
VK,
|
||||
Yandex
|
||||
Yandex,
|
||||
Zoho
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ export default function Instagram (options) {
|
||||
email: null,
|
||||
image: null
|
||||
}
|
||||
}
|
||||
},
|
||||
...options
|
||||
}
|
||||
}
|
||||
|
||||
21
src/providers/kakao.js
Normal file
21
src/providers/kakao.js
Normal file
@@ -0,0 +1,21 @@
|
||||
export default (options) => {
|
||||
return {
|
||||
id: 'kakao',
|
||||
name: 'Kakao',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://kauth.kakao.com/oauth/token',
|
||||
authorizationUrl: 'https://kauth.kakao.com/oauth/authorize?response_type=code',
|
||||
profileUrl: 'https://kapi.kakao.com/v2/user/me',
|
||||
profile: (profile) => {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.kakao_account?.profile.nickname,
|
||||
email: profile.kakao_account?.email,
|
||||
image: profile.kakao_account?.profile.profile_image_url
|
||||
}
|
||||
},
|
||||
...options
|
||||
}
|
||||
}
|
||||
20
src/providers/osso.js
Normal file
20
src/providers/osso.js
Normal file
@@ -0,0 +1,20 @@
|
||||
export default (options) => {
|
||||
return {
|
||||
id: 'osso',
|
||||
name: 'SAML SSO',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: `https://${options.domain}/oauth/token`,
|
||||
authorizationUrl: `https://${options.domain}/oauth/authorize?response_type=code`,
|
||||
profileUrl: `https://${options.domain}/oauth/me`,
|
||||
profile: (profile) => {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.name || profile.email,
|
||||
email: profile.email
|
||||
}
|
||||
},
|
||||
...options
|
||||
}
|
||||
}
|
||||
22
src/providers/zoho.js
Normal file
22
src/providers/zoho.js
Normal file
@@ -0,0 +1,22 @@
|
||||
export default (options) => {
|
||||
return {
|
||||
id: 'zoho',
|
||||
name: 'Zoho',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'AaaServer.profile.Read',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://accounts.zoho.com/oauth/v2/token',
|
||||
authorizationUrl: 'https://accounts.zoho.com/oauth/v2/auth?response_type=code',
|
||||
profileUrl: 'https://accounts.zoho.com/oauth/user/info',
|
||||
profile: (profile) => {
|
||||
return {
|
||||
id: profile.ZUID,
|
||||
name: `${profile.First_Name} ${profile.Last_Name}`,
|
||||
email: profile.Email,
|
||||
image: null
|
||||
}
|
||||
},
|
||||
...options
|
||||
}
|
||||
}
|
||||
@@ -225,18 +225,19 @@ async function NextAuthHandler (req, res, userOptions) {
|
||||
}
|
||||
break
|
||||
case '_log':
|
||||
try {
|
||||
if (!userOptions.logger) return
|
||||
const {
|
||||
code = 'CLIENT_ERROR',
|
||||
level = 'error',
|
||||
message = '[]'
|
||||
} = req.body
|
||||
if (userOptions.logger) {
|
||||
try {
|
||||
const {
|
||||
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[level](code, ...JSON.parse(message))
|
||||
} catch (error) {
|
||||
// If logging itself failed...
|
||||
logger.error('LOGGER_ERROR', error)
|
||||
}
|
||||
}
|
||||
return res.end()
|
||||
default:
|
||||
|
||||
@@ -167,9 +167,17 @@ async function getOAuth2AccessToken (code, provider, codeVerifier) {
|
||||
raw = querystring.parse(data)
|
||||
}
|
||||
|
||||
const accessToken = provider.id === 'slack'
|
||||
? raw.authed_user.access_token
|
||||
: raw.access_token
|
||||
let accessToken
|
||||
if (provider.id === 'slack') {
|
||||
const { ok, error } = raw
|
||||
if (!ok) {
|
||||
return reject(error)
|
||||
}
|
||||
|
||||
accessToken = raw.authed_user.access_token
|
||||
} else {
|
||||
accessToken = raw.access_token
|
||||
}
|
||||
|
||||
resolve({
|
||||
accessToken,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @ts-check
|
||||
import { h } from 'preact' // eslint-disable-line no-unused-vars
|
||||
import render from 'preact-render-to-string'
|
||||
|
||||
/**
|
||||
* Renders an error page.
|
||||
@@ -57,7 +56,7 @@ export default function error ({ baseUrl, basePath, error = 'default', res }) {
|
||||
|
||||
res.status(statusCode)
|
||||
|
||||
return render(
|
||||
return (
|
||||
<div className='error'>
|
||||
<h1>{heading}</h1>
|
||||
<div className='message'>{message}</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import renderToString from 'preact-render-to-string'
|
||||
import signin from './signin'
|
||||
import signout from './signout'
|
||||
import verifyRequest from './verify-request'
|
||||
@@ -10,7 +11,7 @@ export default function renderPage (req, res) {
|
||||
|
||||
res.setHeader('Content-Type', 'text/html')
|
||||
function send ({ html, title }) {
|
||||
res.send(`<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>${css()}</style><title>${title}</title></head><body class="__next-auth-theme-${theme}"><div class="page">${html}</div></body></html>`)
|
||||
res.send(`<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>${css()}</style><title>${title}</title></head><body class="__next-auth-theme-${theme}"><div class="page">${renderToString(html)}</div></body></html>`)
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { h } from 'preact' // eslint-disable-line no-unused-vars
|
||||
import render from 'preact-render-to-string'
|
||||
|
||||
export default function signin ({ csrfToken, providers, callbackUrl, email, error: errorType }) {
|
||||
// We only want to render providers
|
||||
@@ -30,7 +29,7 @@ export default function signin ({ csrfToken, providers, callbackUrl, email, erro
|
||||
|
||||
const error = errorType && (errors[errorType] ?? errors.default)
|
||||
|
||||
return render(
|
||||
return (
|
||||
<div className='signin'>
|
||||
{error &&
|
||||
<div className='error'>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { h } from 'preact' // eslint-disable-line no-unused-vars
|
||||
import render from 'preact-render-to-string'
|
||||
|
||||
export default function signout ({ baseUrl, basePath, csrfToken }) {
|
||||
return render(
|
||||
return (
|
||||
<div className='signout'>
|
||||
<h1>Are you sure you want to sign out?</h1>
|
||||
<form action={`${baseUrl}${basePath}/signout`} method='POST'>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { h } from 'preact' // eslint-disable-line no-unused-vars
|
||||
import render from 'preact-render-to-string'
|
||||
|
||||
export default function verifyRequest ({ baseUrl }) {
|
||||
return render(
|
||||
return (
|
||||
<div className='verify-request'>
|
||||
<h1>Check your email</h1>
|
||||
<p>A sign in link has been sent to your email address.</p>
|
||||
|
||||
40
types/_next.d.ts
vendored
Normal file
40
types/_next.d.ts
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
import { IncomingMessage, ServerResponse } from "http"
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Types from next@10,
|
||||
// see: https://github.com/microsoft/dtslint/issues/297
|
||||
// ------------------------------------------------------
|
||||
export interface NextApiRequest extends IncomingMessage {
|
||||
query: {
|
||||
[key: string]: string | string[]
|
||||
}
|
||||
cookies: {
|
||||
[key: string]: string
|
||||
}
|
||||
body: any
|
||||
env: any
|
||||
preview?: boolean
|
||||
previewData?: any
|
||||
}
|
||||
|
||||
export type Send<T> = (body: T) => void
|
||||
|
||||
export type NextApiResponse<T = any> = ServerResponse & {
|
||||
send: Send<T>
|
||||
json: Send<T>
|
||||
status: (statusCode: number) => NextApiResponse<T>
|
||||
redirect: ((url: string) => NextApiResponse<T>) &
|
||||
((status: number, url: string) => NextApiResponse<T>)
|
||||
setPreviewData: (
|
||||
data: object | string,
|
||||
options?: {
|
||||
maxAge?: number
|
||||
}
|
||||
) => NextApiResponse<T>
|
||||
clearPreviewData: () => NextApiResponse<T>
|
||||
}
|
||||
|
||||
export type NextApiHandler<T = any> = (
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<T>
|
||||
) => void | Promise<void>
|
||||
14
types/_utils.d.ts
vendored
Normal file
14
types/_utils.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
export type NonNullParams<T> = {
|
||||
[K in keyof T]: T[K] extends Record<string, unknown>
|
||||
? NonNullParams<T[K]>
|
||||
: NonNullable<T[K]>
|
||||
}
|
||||
|
||||
export type NullableParams<T> = {
|
||||
[K in keyof T]: T[K] | undefined | null
|
||||
}
|
||||
|
||||
export type WithAdditionalParams<T extends Record<string, any>> = T &
|
||||
Record<string, unknown>
|
||||
|
||||
export type Awaitable<T> = T | PromiseLike<T>
|
||||
242
types/adapters.d.ts
vendored
Normal file
242
types/adapters.d.ts
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
import { ConnectionOptions, EntitySchema } from "typeorm"
|
||||
import { AppOptions, User } from "."
|
||||
import { AppProvider } from "./providers"
|
||||
|
||||
export interface Profile {
|
||||
id: string
|
||||
name: string
|
||||
email: string | null
|
||||
image?: string | null
|
||||
}
|
||||
|
||||
export interface Session {
|
||||
userId: string | number | object
|
||||
expires: Date
|
||||
sessionToken: string
|
||||
accessToken: string
|
||||
}
|
||||
|
||||
export interface VerificationRequest {
|
||||
identifier: string
|
||||
token: string
|
||||
expires: Date
|
||||
}
|
||||
|
||||
export interface SendVerificationRequestParams {
|
||||
identifier: string
|
||||
url: string
|
||||
token: string
|
||||
baseUrl: string
|
||||
provider: AppProvider
|
||||
}
|
||||
|
||||
export type EmailAppProvider = AppProvider & {
|
||||
sendVerificationRequest: (
|
||||
params: SendVerificationRequestParams
|
||||
) => Promise<void>
|
||||
maxAge: number | undefined
|
||||
}
|
||||
|
||||
export interface AdapterInstance<
|
||||
TUser,
|
||||
TProfile,
|
||||
TSession,
|
||||
TVerificationRequest
|
||||
> {
|
||||
createUser: (profile: TProfile) => Promise<TUser>
|
||||
getUser: (id: string) => Promise<TUser | null>
|
||||
getUserByEmail: (email: string) => Promise<TUser | null>
|
||||
getUserByProviderAccountId: (
|
||||
providerId: string,
|
||||
providerAccountId: string
|
||||
) => Promise<TUser | null>
|
||||
updateUser: (user: TUser) => Promise<TUser>
|
||||
linkAccount: (
|
||||
userId: string,
|
||||
providerId: string,
|
||||
providerType: string,
|
||||
providerAccountId: string,
|
||||
refreshToken: string,
|
||||
accessToken: string,
|
||||
accessTokenExpires: number
|
||||
) => Promise<void>
|
||||
createSession: (user: TUser) => Promise<TSession>
|
||||
getSession: (sessionToken: string) => Promise<TSession | null>
|
||||
updateSession: (session: TSession, force?: boolean) => Promise<TSession>
|
||||
deleteSession: (sessionToken: string) => Promise<void>
|
||||
createVerificationRequest?: (
|
||||
email: string,
|
||||
url: string,
|
||||
token: string,
|
||||
secret: string,
|
||||
provider: EmailAppProvider,
|
||||
options: AppOptions
|
||||
) => Promise<TVerificationRequest>
|
||||
getVerificationRequest?: (
|
||||
email: string,
|
||||
verificationToken: string,
|
||||
secret: string,
|
||||
provider: AppProvider
|
||||
) => Promise<TVerificationRequest | null>
|
||||
deleteVerificationRequest?: (
|
||||
email: string,
|
||||
verificationToken: string,
|
||||
secret: string,
|
||||
provider: AppProvider
|
||||
) => Promise<void>
|
||||
}
|
||||
|
||||
interface Adapter<
|
||||
TUser extends User = any,
|
||||
TProfile extends Profile = any,
|
||||
TSession extends Session = any,
|
||||
TVerificationRequest extends VerificationRequest = any
|
||||
> {
|
||||
getAdapter: (
|
||||
appOptions: AppOptions
|
||||
) => Promise<AdapterInstance<TUser, TProfile, TSession, TVerificationRequest>>
|
||||
}
|
||||
|
||||
type Schema<T = any> = EntitySchema<T>["options"]
|
||||
|
||||
interface Adapters {
|
||||
Default: TypeORMAdapter["Adapter"]
|
||||
TypeORM: TypeORMAdapter
|
||||
Prisma: PrismaAdapter
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: fix auto-type schema
|
||||
*/
|
||||
|
||||
interface TypeORMAdapter<
|
||||
A extends TypeORMAccountModel = any,
|
||||
U extends TypeORMUserModel = any,
|
||||
S extends TypeORMSessionModel = any,
|
||||
VR extends TypeORMVerificationRequestModel = any
|
||||
> {
|
||||
Adapter: (
|
||||
typeOrmConfig: ConnectionOptions,
|
||||
options?: {
|
||||
models?: {
|
||||
Account?: {
|
||||
model: A
|
||||
schema: Schema<A>
|
||||
}
|
||||
User?: {
|
||||
model: U
|
||||
schema: Schema<U>
|
||||
}
|
||||
Session?: {
|
||||
model: S
|
||||
schema: Schema<S>
|
||||
}
|
||||
VerificationRequest?: {
|
||||
model: VR
|
||||
schema: Schema<VR>
|
||||
}
|
||||
}
|
||||
}
|
||||
) => Adapter<U, Profile, S, VR>
|
||||
Models: {
|
||||
Account: {
|
||||
model: TypeORMAccountModel
|
||||
schema: Schema<TypeORMAccountModel>
|
||||
}
|
||||
User: {
|
||||
model: TypeORMUserModel
|
||||
schema: Schema<TypeORMUserModel>
|
||||
}
|
||||
Session: {
|
||||
model: TypeORMSessionModel
|
||||
schema: Schema<TypeORMSessionModel>
|
||||
}
|
||||
VerificationRequest: {
|
||||
model: TypeORMVerificationRequestModel
|
||||
schema: Schema<TypeORMVerificationRequestModel>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface PrismaAdapter {
|
||||
Adapter: (config: {
|
||||
prisma: any
|
||||
modelMapping?: {
|
||||
User: string
|
||||
Account: string
|
||||
Session: string
|
||||
VerificationRequest: string
|
||||
}
|
||||
}) => Adapter
|
||||
}
|
||||
|
||||
declare const Adapters: Adapters
|
||||
|
||||
declare class TypeORMAccountModel {
|
||||
compoundId: string
|
||||
userId: number
|
||||
providerType: string
|
||||
providerId: string
|
||||
providerAccountId: string
|
||||
refreshToken?: string
|
||||
accessToken?: string
|
||||
accessTokenExpires?: Date
|
||||
|
||||
constructor(
|
||||
userId: number,
|
||||
providerId: string,
|
||||
providerType: string,
|
||||
providerAccountId: string,
|
||||
refreshToken?: string,
|
||||
accessToken?: string,
|
||||
accessTokenExpires?: Date
|
||||
)
|
||||
}
|
||||
|
||||
declare class TypeORMUserModel implements User {
|
||||
name?: string
|
||||
email?: string
|
||||
image?: string
|
||||
emailVerified?: Date
|
||||
|
||||
constructor(
|
||||
name?: string,
|
||||
email?: string,
|
||||
image?: string,
|
||||
emailVerified?: Date
|
||||
)
|
||||
}
|
||||
|
||||
declare class TypeORMSessionModel implements Session {
|
||||
userId: number
|
||||
expires: Date
|
||||
sessionToken: string
|
||||
accessToken: string
|
||||
|
||||
constructor(
|
||||
userId: number,
|
||||
expires: Date,
|
||||
sessionToken?: string,
|
||||
accessToken?: string
|
||||
)
|
||||
}
|
||||
|
||||
declare class TypeORMVerificationRequestModel implements VerificationRequest {
|
||||
identifier: string
|
||||
token: string
|
||||
expires: Date
|
||||
|
||||
constructor(identifier: string, token: string, expires: Date)
|
||||
}
|
||||
|
||||
export default Adapters
|
||||
export {
|
||||
Adapter,
|
||||
Adapters,
|
||||
TypeORMAdapter,
|
||||
TypeORMAccountModel,
|
||||
TypeORMUserModel,
|
||||
TypeORMSessionModel,
|
||||
TypeORMVerificationRequestModel,
|
||||
PrismaAdapter,
|
||||
}
|
||||
97
types/client.d.ts
vendored
Normal file
97
types/client.d.ts
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
import { FC } from "react"
|
||||
import { IncomingMessage } from "http"
|
||||
import { WithAdditionalParams } from "./_utils"
|
||||
import { Session } from "."
|
||||
import { AppProvider, DefaultProviders, Providers } from "./providers"
|
||||
|
||||
interface ContextProviderProps {
|
||||
session: WithAdditionalParams<Session> | null | undefined
|
||||
options?: SetOptionsParams
|
||||
}
|
||||
|
||||
interface SetOptionsParams {
|
||||
baseUrl?: string
|
||||
basePath?: string
|
||||
clientMaxAge?: number
|
||||
keepAlive?: number
|
||||
}
|
||||
|
||||
interface SignInResponse {
|
||||
error: string | undefined
|
||||
status: number
|
||||
ok: boolean
|
||||
url: string | null
|
||||
}
|
||||
|
||||
type ContextProvider = FC<ContextProviderProps>
|
||||
|
||||
interface NextContext {
|
||||
req?: IncomingMessage
|
||||
ctx?: { req: IncomingMessage }
|
||||
}
|
||||
|
||||
declare function useSession(): [Session | null | undefined, boolean]
|
||||
|
||||
declare function providers(): Promise<Record<
|
||||
keyof DefaultProviders | string,
|
||||
AppProvider
|
||||
> | null>
|
||||
declare const getProviders: typeof providers
|
||||
declare function session(
|
||||
context?: NextContext & {
|
||||
triggerEvent?: boolean
|
||||
}
|
||||
): Promise<Session | null>
|
||||
declare const getSession: typeof session
|
||||
declare function csrfToken(context?: NextContext): Promise<string | null>
|
||||
declare const getCsrfToken: typeof csrfToken
|
||||
declare function signin(
|
||||
provider: "credentials" | "email",
|
||||
data?: Record<string, unknown> & {
|
||||
callbackUrl?: string
|
||||
redirect?: false
|
||||
},
|
||||
authorizationParams?:
|
||||
| string
|
||||
| string[][]
|
||||
| Record<string, unknown>
|
||||
| URLSearchParams
|
||||
): Promise<SignInResponse>
|
||||
declare function signin(
|
||||
provider?: string,
|
||||
data?: Record<string, unknown> & {
|
||||
callbackUrl?: string
|
||||
redirect?: boolean
|
||||
},
|
||||
authorizationParams?:
|
||||
| string
|
||||
| string[][]
|
||||
| Record<string, unknown>
|
||||
| URLSearchParams
|
||||
): Promise<void>
|
||||
declare const signIn: typeof signin
|
||||
declare function signout(data?: {
|
||||
callbackUrl?: string
|
||||
redirect?: boolean
|
||||
}): Promise<void>
|
||||
declare const signOut: typeof signout
|
||||
declare function options(options: SetOptionsParams): void
|
||||
declare const setOptions: typeof options
|
||||
declare const Provider: ContextProvider
|
||||
|
||||
export {
|
||||
useSession,
|
||||
session,
|
||||
getSession,
|
||||
providers,
|
||||
getProviders,
|
||||
csrfToken,
|
||||
getCsrfToken,
|
||||
signin,
|
||||
signIn,
|
||||
signout,
|
||||
signOut,
|
||||
options,
|
||||
setOptions,
|
||||
Provider,
|
||||
}
|
||||
173
types/index.d.ts
vendored
Normal file
173
types/index.d.ts
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
// Minimum TypeScript Version: 3.5
|
||||
|
||||
/// <reference types="node" />
|
||||
|
||||
import { ConnectionOptions } from "typeorm"
|
||||
import { Adapter } from "./adapters"
|
||||
import { JWTOptions, JWT } from "./jwt"
|
||||
import { AppProvider, Providers } from "./providers"
|
||||
import { NextApiRequest, NextApiResponse, NextApiHandler } from "./_next"
|
||||
import { Awaitable, NonNullParams, WithAdditionalParams } from "./_utils"
|
||||
|
||||
export interface NextAuthOptions {
|
||||
providers: Providers
|
||||
database?: string | Record<string, any> | ConnectionOptions
|
||||
secret?: string
|
||||
session?: SessionOptions
|
||||
jwt?: JWTOptions
|
||||
pages?: PagesOptions
|
||||
callbacks?: CallbacksOptions
|
||||
debug?: boolean
|
||||
adapter?: Adapter
|
||||
events?: EventsOptions
|
||||
useSecureCookies?: boolean
|
||||
cookies?: CookiesOptions
|
||||
logger?: LoggerInstance
|
||||
theme?: "light" | "dark" | "auto"
|
||||
}
|
||||
|
||||
export interface LoggerInstance {
|
||||
warn: (code?: string, ...message: unknown[]) => void
|
||||
error: (code?: string, ...message: unknown[]) => void
|
||||
debug: (code?: string, ...message: unknown[]) => void
|
||||
}
|
||||
|
||||
interface InternalOptions
|
||||
extends Omit<
|
||||
NextAuthOptions,
|
||||
"providers" | "database" | "session" | "useSecureCookie"
|
||||
> {
|
||||
pkce: {
|
||||
code_verifier?: string
|
||||
code_challenge_method?: "S256"
|
||||
}
|
||||
provider?: string
|
||||
baseUrl?: string
|
||||
basePath?: string
|
||||
action?:
|
||||
| "providers"
|
||||
| "session"
|
||||
| "csrf"
|
||||
| "signin"
|
||||
| "signout"
|
||||
| "callback"
|
||||
| "verify-request"
|
||||
| "error"
|
||||
csrfToken?: string
|
||||
}
|
||||
|
||||
export interface AppOptions
|
||||
extends Omit<NextApiRequest, "cookies">,
|
||||
NonNullParams<InternalOptions> {
|
||||
providers: AppProvider[]
|
||||
}
|
||||
|
||||
export interface Account extends Record<string, unknown> {
|
||||
accessToken: string
|
||||
idToken?: string
|
||||
refreshToken?: string
|
||||
access_token: string
|
||||
expires_in?: number | null
|
||||
refresh_token?: string
|
||||
id_token?: string
|
||||
id: string
|
||||
provider: string
|
||||
type: string
|
||||
}
|
||||
export interface Profile extends Record<string, unknown> {}
|
||||
|
||||
export interface CallbacksOptions<
|
||||
P extends Record<string, unknown> = Profile,
|
||||
A extends Record<string, unknown> = Account
|
||||
> {
|
||||
signIn?(user: User, account: A, profile: P): Awaitable<string | boolean>
|
||||
redirect?(url: string, baseUrl: string): Awaitable<string>
|
||||
session?(session: Session, userOrToken: JWT | User): Awaitable<Session>
|
||||
jwt?(
|
||||
token: JWT,
|
||||
user?: User,
|
||||
account?: A,
|
||||
profile?: P,
|
||||
isNewUser?: boolean
|
||||
): Awaitable<JWT>
|
||||
}
|
||||
|
||||
export interface CookieOption {
|
||||
name: string
|
||||
options: {
|
||||
httpOnly: boolean
|
||||
sameSite: true | "strict" | "lax" | "none"
|
||||
path?: string
|
||||
secure: boolean
|
||||
maxAge?: number
|
||||
domain?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface CookiesOptions {
|
||||
sessionToken?: CookieOption
|
||||
callbackUrl?: CookieOption
|
||||
csrfToken?: CookieOption
|
||||
pkceCodeVerifier?: CookieOption
|
||||
}
|
||||
|
||||
export type EventType =
|
||||
| "signIn"
|
||||
| "signOut"
|
||||
| "createUser"
|
||||
| "updateUser"
|
||||
| "linkAccount"
|
||||
| "session"
|
||||
| "error"
|
||||
|
||||
export type EventCallback = (message: any) => Promise<void>
|
||||
|
||||
export type EventsOptions = Partial<Record<EventType, EventCallback>>
|
||||
|
||||
export interface PagesOptions {
|
||||
signIn?: string
|
||||
signOut?: string
|
||||
error?: string
|
||||
verifyRequest?: string
|
||||
newUser?: string | null
|
||||
}
|
||||
|
||||
export interface Session {
|
||||
user: WithAdditionalParams<User>
|
||||
accessToken?: string
|
||||
expires: string
|
||||
}
|
||||
|
||||
export interface SessionOptions {
|
||||
jwt?: boolean
|
||||
maxAge?: number
|
||||
updateAge?: number
|
||||
}
|
||||
|
||||
export interface User {
|
||||
name?: string | null
|
||||
email?: string | null
|
||||
image?: string | null
|
||||
}
|
||||
|
||||
export interface NextAuthRequest extends NextApiRequest {
|
||||
options: InternalOptions
|
||||
}
|
||||
export type NextAuthResponse = NextApiResponse
|
||||
|
||||
declare function NextAuthHandler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
options?: NextAuthOptions
|
||||
): ReturnType<NextApiHandler>
|
||||
declare function NextAuth(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
options?: NextAuthOptions
|
||||
): ReturnType<NextApiHandler>
|
||||
declare function NextAuth(
|
||||
options: NextAuthOptions
|
||||
): ReturnType<typeof NextAuthHandler>
|
||||
|
||||
export { NextAuthHandler, NextAuth }
|
||||
export default NextAuth
|
||||
67
types/jwt.d.ts
vendored
Normal file
67
types/jwt.d.ts
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
import { JWT, JWE } from "jose"
|
||||
import { NextApiRequest } from "./_next"
|
||||
import { WithAdditionalParams } from "./_utils"
|
||||
|
||||
export interface JWT extends Record<string, unknown> {
|
||||
name?: string | null
|
||||
email?: string | null
|
||||
picture?: string | null
|
||||
}
|
||||
|
||||
export interface JWTEncodeParams {
|
||||
token?: WithAdditionalParams<JWT>
|
||||
maxAge?: number
|
||||
secret: string | Buffer
|
||||
signingKey?: string
|
||||
signingOptions?: JWT.SignOptions
|
||||
encryptionKey?: string
|
||||
encryptionOptions?: object
|
||||
encryption?: boolean
|
||||
}
|
||||
|
||||
export interface JWTDecodeParams {
|
||||
token?: string
|
||||
maxAge?: number
|
||||
secret: string | Buffer
|
||||
signingKey?: string
|
||||
verificationKey?: string
|
||||
verificationOptions?: JWT.VerifyOptions<false>
|
||||
encryptionKey?: string
|
||||
decryptionKey?: string
|
||||
decryptionOptions?: JWE.DecryptOptions<false>
|
||||
encryption?: boolean
|
||||
}
|
||||
|
||||
export interface JWTOptions {
|
||||
secret?: string
|
||||
maxAge?: number
|
||||
encryption?: boolean
|
||||
signingKey?: string
|
||||
encryptionKey?: string
|
||||
encode?: (options: JWTEncodeParams) => Promise<string>
|
||||
decode?: (options: JWTDecodeParams) => Promise<WithAdditionalParams<JWT>>
|
||||
}
|
||||
|
||||
declare function encode(args?: JWTEncodeParams): Promise<string>
|
||||
|
||||
declare function decode(
|
||||
args?: JWTDecodeParams & { token: string }
|
||||
): Promise<WithAdditionalParams<JWT>>
|
||||
|
||||
declare function getToken(
|
||||
args?: {
|
||||
req: NextApiRequest
|
||||
secureCookie?: boolean
|
||||
cookieName?: string
|
||||
raw?: string
|
||||
} & JWTDecodeParams
|
||||
): Promise<WithAdditionalParams<JWT>>
|
||||
|
||||
declare function getToken(args?: {
|
||||
req: NextApiRequest
|
||||
secureCookie?: boolean
|
||||
cookieName?: string
|
||||
raw: true
|
||||
}): Promise<string>
|
||||
|
||||
export { encode, decode, getToken }
|
||||
435
types/providers.d.ts
vendored
Normal file
435
types/providers.d.ts
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
import { User } from "."
|
||||
import { JWT } from "./jwt"
|
||||
import { NonNullParams, NullableParams, WithAdditionalParams } from "./_utils"
|
||||
|
||||
export interface Provider<
|
||||
T extends string | undefined = undefined,
|
||||
U = T extends string ? "oauth" : string
|
||||
> {
|
||||
id: T
|
||||
name: string
|
||||
type: U extends string ? U : "oauth" | "email" | "credentials"
|
||||
version: string
|
||||
scope: string
|
||||
params: { grant_type: string }
|
||||
accessTokenUrl: string
|
||||
requestTokenUrl: string
|
||||
authorizationUrl: string
|
||||
profileUrl: string
|
||||
profile: (
|
||||
profile: Record<string, any>,
|
||||
tokens: any
|
||||
) => (User & { id: string }) | Promise<User & { id: string }>
|
||||
clientId: string
|
||||
clientSecret: string | Record<string, unknown>
|
||||
idToken?: boolean
|
||||
}
|
||||
|
||||
export interface AppProvider extends Pick<Provider, "id" | "name" | "type"> {
|
||||
signinUrl: string
|
||||
callbackUrl: string
|
||||
}
|
||||
|
||||
export interface DefaultProviders {
|
||||
Apple: Apple
|
||||
Attlassian: Atlassian
|
||||
Auth0: Auth0
|
||||
AzureADB2C: AzureADB2C
|
||||
Basecamp: Basecamp
|
||||
BattleNet: BattleNet
|
||||
Box: Box
|
||||
Bungie: Bungie
|
||||
Cognito: Cognito
|
||||
Credentials: Credentials
|
||||
Discord: Discord
|
||||
Email: Email
|
||||
EVEOnline: EVEOnline
|
||||
Facebook: Facebook
|
||||
FACEIT: FACEIT
|
||||
Foursquare: Foursquare
|
||||
FusionAuth: FusionAuth
|
||||
GitHub: GitHub
|
||||
GitLab: GitLab
|
||||
Google: Google
|
||||
IdentityServer4: IdentityServer4
|
||||
Instagram: Instagram
|
||||
Kakao: Kakao
|
||||
LINE: LINE
|
||||
LinkedIn: LinkedIn
|
||||
MailRu: MailRu
|
||||
Medium: Medium
|
||||
Netlify: Netlify
|
||||
Okta: Okta
|
||||
Osso: Osso
|
||||
Reddit: Reddit
|
||||
Salesforce: Salesforce
|
||||
Slack: Slack
|
||||
Spotify: Spotify
|
||||
Strava: Strava
|
||||
Twitch: Twitch
|
||||
Twitter: Twitter
|
||||
VK: VK
|
||||
Yandex: Yandex
|
||||
Zoho: Zoho
|
||||
}
|
||||
|
||||
export type Providers = Array<
|
||||
Provider | ReturnType<DefaultProviders[keyof DefaultProviders]>
|
||||
>
|
||||
|
||||
declare const Providers: DefaultProviders
|
||||
|
||||
export default Providers
|
||||
|
||||
/**
|
||||
* Email
|
||||
*/
|
||||
type Email = (
|
||||
options: ProviderEmailOptions
|
||||
) => NonNullParams<ProviderEmailOptions> & { id: "email"; type: "email" }
|
||||
|
||||
interface VerificationRequestParams extends Provider {
|
||||
identifier: string
|
||||
url: string
|
||||
baseUrl: string
|
||||
token: string
|
||||
provider: ProviderEmailOptions
|
||||
}
|
||||
|
||||
interface ProviderEmailOptions {
|
||||
name?: string
|
||||
server?: string | ProviderEmailServer
|
||||
from?: string
|
||||
maxAge?: number
|
||||
sendVerificationRequest?: (
|
||||
options: VerificationRequestParams
|
||||
) => Promise<void>
|
||||
}
|
||||
|
||||
interface ProviderEmailServer {
|
||||
host: string
|
||||
port: number
|
||||
auth: {
|
||||
user: string
|
||||
pass: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Credentials
|
||||
*/
|
||||
type Credentials = (
|
||||
options: ProviderCredentialsOptions
|
||||
) => NonNullParams<ProviderCredentialsOptions> & {
|
||||
id: "credentials"
|
||||
type: "credentials"
|
||||
}
|
||||
|
||||
interface ProviderCredentialsOptions {
|
||||
id?: string
|
||||
name: string
|
||||
credentials: CredentialInput
|
||||
authorize: (credentials: Record<string, string>) => Promise<User | null>
|
||||
}
|
||||
|
||||
interface CredentialInput {
|
||||
[key: string]: {
|
||||
label?: string
|
||||
type?: string
|
||||
value?: string
|
||||
placeholder?: string
|
||||
}
|
||||
}
|
||||
|
||||
type OptionsBase = {
|
||||
[K in keyof Omit<Provider, "id">]?: Provider[K]
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider options
|
||||
* @link https://next-auth.js.org/configuration/providers#oauth-provider-options
|
||||
*/
|
||||
interface ProviderCommonOptions extends OptionsBase {
|
||||
authorizationParams?: Record<string, string>
|
||||
clientId: string
|
||||
clientSecret: string
|
||||
headers?: Record<string, any>
|
||||
idToken?: boolean
|
||||
name?: string
|
||||
protection?: "pkce" | "state" | "both" | "none"
|
||||
state?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Apple
|
||||
*/
|
||||
type Apple = (
|
||||
options: ProviderAppleOptions
|
||||
) => Provider<"apple"> & { protection: "none" }
|
||||
|
||||
interface ProviderAppleOptions
|
||||
extends Omit<ProviderCommonOptions, "clientSecret"> {
|
||||
name?: string
|
||||
clientId: string
|
||||
clientSecret: Record<"appleId" | "teamId" | "privateKey" | "keyId", string>
|
||||
}
|
||||
|
||||
interface ProviderAppleSecret {
|
||||
appleId: string
|
||||
teamId: string
|
||||
privateKey: string
|
||||
keyId: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Twitter
|
||||
*/
|
||||
type Twitter = (options: ProviderCommonOptions) => Provider<"twitter">
|
||||
|
||||
/**
|
||||
* Facebook
|
||||
*/
|
||||
type Facebook = (options: ProviderCommonOptions) => Provider<"facebook">
|
||||
|
||||
/**
|
||||
* GitHub
|
||||
*/
|
||||
type GitHub = (options: ProviderGitHubOptions) => Provider<"github">
|
||||
|
||||
interface ProviderGitHubOptions extends Omit<ProviderCommonOptions, "scope"> {
|
||||
scope?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* GitLab
|
||||
*/
|
||||
type GitLab = (options: ProviderCommonOptions) => Provider<"gitlab">
|
||||
|
||||
/**
|
||||
* Slack
|
||||
*/
|
||||
type Slack = (options: ProviderCommonOptions) => Provider<"slack">
|
||||
|
||||
/**
|
||||
* Google
|
||||
*/
|
||||
type Google = (options: ProviderGoogleOptions) => Provider<"google">
|
||||
|
||||
interface ProviderGoogleOptions extends ProviderCommonOptions {
|
||||
authorizationUrl?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth0
|
||||
*/
|
||||
type Auth0 = (
|
||||
options: ProviderAuth0Options
|
||||
) => Provider<"auth0"> & { domain: string }
|
||||
|
||||
interface ProviderAuth0Options extends Omit<ProviderCommonOptions, "profile"> {
|
||||
domain: string
|
||||
profile?: (profile: Auth0Profile) => User & { id: string }
|
||||
}
|
||||
|
||||
interface Auth0Profile {
|
||||
sub: string
|
||||
nickname: string
|
||||
email: string
|
||||
picture: string
|
||||
}
|
||||
|
||||
/**
|
||||
* IS4
|
||||
*/
|
||||
|
||||
type IdentityServer4 = (
|
||||
options: ProviderIS4Options
|
||||
) => Provider<"identity-server4" | string> & { domain: string }
|
||||
|
||||
interface ProviderIS4Options extends Omit<ProviderCommonOptions, "id"> {
|
||||
id: string
|
||||
scope: string
|
||||
domain: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Discord
|
||||
*/
|
||||
type Discord = (options: ProviderCommonOptions) => Provider<"discord">
|
||||
|
||||
/**
|
||||
* Twitch
|
||||
*/
|
||||
type Twitch = (options: ProviderCommonOptions) => Provider<"twitch">
|
||||
|
||||
/**
|
||||
* Okta
|
||||
*/
|
||||
type Okta = (
|
||||
options: ProviderOktaOptions
|
||||
) => Provider<"okta"> & { domain: string }
|
||||
|
||||
interface ProviderOktaOptions extends ProviderCommonOptions {
|
||||
domain: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Battle.net
|
||||
*/
|
||||
type BattleNet = (
|
||||
options: ProviderBattleNetOptions
|
||||
) => Provider<"battlenet"> & { region: string }
|
||||
|
||||
interface ProviderBattleNetOptions extends ProviderCommonOptions {
|
||||
region: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Box
|
||||
*/
|
||||
type Box = (options: ProviderCommonOptions) => Provider<"box">
|
||||
|
||||
/**
|
||||
* Cognito
|
||||
*/
|
||||
type Cognito = (
|
||||
options: ProviderCognitoOptions
|
||||
) => Provider<"cognito"> & { domain: string }
|
||||
|
||||
interface ProviderCognitoOptions extends ProviderCommonOptions {
|
||||
domain: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Yandex
|
||||
*/
|
||||
type Yandex = (options: ProviderCommonOptions) => Provider<"yandex">
|
||||
|
||||
/**
|
||||
* LinkedIn
|
||||
*/
|
||||
type LinkedIn = (options: ProviderLinkedInOptions) => Provider<"linkedin">
|
||||
|
||||
interface ProviderLinkedInOptions extends ProviderCommonOptions {
|
||||
scope?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Spotify
|
||||
*/
|
||||
type Spotify = (options: ProviderSpotifyOptions) => Provider<"spotify">
|
||||
|
||||
interface ProviderSpotifyOptions extends ProviderCommonOptions {
|
||||
scope?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Basecamp
|
||||
*/
|
||||
type Basecamp = (options: ProviderCommonOptions) => Provider<"basecamp">
|
||||
|
||||
/**
|
||||
* Reddit
|
||||
*/
|
||||
type Reddit = (options: ProviderCommonOptions) => Provider<"reddit">
|
||||
|
||||
/**
|
||||
* Atlassian
|
||||
*/
|
||||
type Atlassian = (options: ProviderCommonOptions) => Provider<"atlassian">
|
||||
|
||||
/**
|
||||
* AzureADB2C
|
||||
*/
|
||||
type AzureADB2C = (
|
||||
options: ProviderAzureADB2COptions
|
||||
) => Provider<"azure-ad-b2c">
|
||||
|
||||
interface ProviderAzureADB2COptions extends ProviderCommonOptions {
|
||||
tenantId?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Bungie
|
||||
*/
|
||||
type Bungie = (options: ProviderCommonOptions) => Provider<"bungie">
|
||||
|
||||
/**
|
||||
* EVEOnline
|
||||
*/
|
||||
type EVEOnline = (options: ProviderCommonOptions) => Provider<"eveonline">
|
||||
|
||||
/**
|
||||
* FACEIT
|
||||
*/
|
||||
type FACEIT = (options: ProviderCommonOptions) => Provider<"faceit">
|
||||
|
||||
/**
|
||||
* Foursquare
|
||||
*/
|
||||
type Foursquare = (options: ProviderCommonOptions) => Provider<"foursquare">
|
||||
|
||||
/**
|
||||
* FusionAuth
|
||||
*/
|
||||
type FusionAuth = (options: ProviderFusionAuthOptions) => Provider<"fusionauth">
|
||||
|
||||
interface ProviderFusionAuthOptions extends ProviderCommonOptions {
|
||||
tenantId?: string
|
||||
domain?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Instagram
|
||||
*/
|
||||
type Instagram = (options: ProviderCommonOptions) => Provider<"instagram">
|
||||
|
||||
/**
|
||||
* Kakao
|
||||
*/
|
||||
type Kakao = (options: ProviderCommonOptions) => Provider<"kakao">
|
||||
|
||||
/**
|
||||
* LINE
|
||||
*/
|
||||
type LINE = (options: ProviderCommonOptions) => Provider<"line">
|
||||
|
||||
/**
|
||||
* MailRu
|
||||
*/
|
||||
type MailRu = (options: ProviderCommonOptions) => Provider<"mailru">
|
||||
|
||||
/**
|
||||
* Medium
|
||||
*/
|
||||
type Medium = (options: ProviderCommonOptions) => Provider<"medium">
|
||||
|
||||
/**
|
||||
* Netlify
|
||||
*/
|
||||
type Netlify = (options: ProviderCommonOptions) => Provider<"netlify">
|
||||
|
||||
/**
|
||||
* Osso
|
||||
*/
|
||||
type Osso = (options: ProviderCommonOptions) => Provider<"osso">
|
||||
|
||||
/**
|
||||
* Salesforce
|
||||
*/
|
||||
type Salesforce = (options: ProviderCommonOptions) => Provider<"salesforce">
|
||||
|
||||
/**
|
||||
* Strava
|
||||
*/
|
||||
type Strava = (options: ProviderCommonOptions) => Provider<"strava">
|
||||
|
||||
/**
|
||||
* VK
|
||||
*/
|
||||
type VK = (options: ProviderCommonOptions) => Provider<"vk">
|
||||
|
||||
/**
|
||||
* Zoho
|
||||
*/
|
||||
type Zoho = (options: ProviderCommonOptions) => Provider<"zoho">
|
||||
26
types/tests/adapters.test.ts
Normal file
26
types/tests/adapters.test.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import Adapters, { TypeORMAdapter } from "next-auth/adapters"
|
||||
|
||||
// ExpectType TypeORMAdapter["Adapter"]
|
||||
Adapters.Default({
|
||||
type: "sqlite",
|
||||
database: ":memory:",
|
||||
synchronize: true,
|
||||
})
|
||||
|
||||
// ExpectType TypeORMAdapter
|
||||
Adapters.TypeORM.Adapter({
|
||||
type: "sqlite",
|
||||
database: ":memory:",
|
||||
synchronize: true,
|
||||
})
|
||||
|
||||
// ExpectType PrismaAdapter
|
||||
Adapters.Prisma.Adapter({
|
||||
prisma: {},
|
||||
modelMapping: {
|
||||
User: "foo",
|
||||
Account: "bar",
|
||||
Session: "session",
|
||||
VerificationRequest: "foo",
|
||||
},
|
||||
})
|
||||
83
types/tests/client.test.ts
Normal file
83
types/tests/client.test.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import * as client from "next-auth/client"
|
||||
import { nextReq } from "./test-helpers"
|
||||
|
||||
const clientSession = {
|
||||
user: {
|
||||
name: "Bruce",
|
||||
email: "bruce@lee.com",
|
||||
image: "path/to/img",
|
||||
},
|
||||
accessToken: "123z",
|
||||
expires: "1234",
|
||||
}
|
||||
|
||||
// $ExpectType [Session | null | undefined, boolean]
|
||||
client.useSession()
|
||||
|
||||
// $ExpectType Promise<Session | null>
|
||||
client.getSession({ req: nextReq })
|
||||
|
||||
// $ExpectType Promise<Session | null>
|
||||
client.session({ req: nextReq })
|
||||
|
||||
// $ExpectType Promise<Record<string, AppProvider> | null>
|
||||
client.getProviders()
|
||||
|
||||
// $ExpectType Promise<Record<string, AppProvider> | null>
|
||||
client.providers()
|
||||
|
||||
// $ExpectType Promise<string | null>
|
||||
client.getCsrfToken({ req: nextReq })
|
||||
|
||||
// $ExpectType Promise<string | null>
|
||||
client.csrfToken({ req: nextReq })
|
||||
|
||||
// $ExpectType Promise<void>
|
||||
client.signin("github", { data: "foo", redirect: false }, { login: "username" })
|
||||
|
||||
// $ExpectType Promise<SignInResponse>
|
||||
client.signin("credentials", { data: "foo", redirect: false })
|
||||
|
||||
// $ExpectType Promise<SignInResponse>
|
||||
client.signin("email", { data: "foo", redirect: false })
|
||||
|
||||
// $ExpectType Promise<void>
|
||||
client.signin("email", { data: "foo", redirect: true })
|
||||
|
||||
// $ExpectType Promise<void>
|
||||
client.signout()
|
||||
|
||||
// $ExpectType Promise<void>
|
||||
client.signout({ callbackUrl: "https://foo.com/callback", redirect: true })
|
||||
|
||||
// $ExpectType ReactElement<any, any> | null
|
||||
client.Provider({
|
||||
session: clientSession,
|
||||
options: {
|
||||
baseUrl: "https://foo.com",
|
||||
basePath: "/",
|
||||
clientMaxAge: 1234,
|
||||
},
|
||||
})
|
||||
|
||||
// $ExpectType ReactElement<any, any> | null
|
||||
client.Provider({
|
||||
session: clientSession,
|
||||
})
|
||||
|
||||
// $ExpectType ReactElement<any, any> | null
|
||||
client.Provider({
|
||||
session: undefined,
|
||||
options: {},
|
||||
})
|
||||
|
||||
// $ExpectType ReactElement<any, any> | null
|
||||
client.Provider({
|
||||
session: null,
|
||||
options: {
|
||||
baseUrl: "https://foo.com",
|
||||
basePath: "/",
|
||||
clientMaxAge: 1234,
|
||||
keepAlive: 4321,
|
||||
},
|
||||
})
|
||||
26
types/tests/jwt.test.ts
Normal file
26
types/tests/jwt.test.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import * as JWTType from "next-auth/jwt"
|
||||
import { nextReq } from "./test-helpers"
|
||||
|
||||
// $ExpectType Promise<string>
|
||||
JWTType.encode({
|
||||
token: { key: "value" },
|
||||
secret: "secret",
|
||||
})
|
||||
|
||||
// $ExpectType Promise<WithAdditionalParams<JWT>>
|
||||
JWTType.decode({
|
||||
token: "token",
|
||||
secret: "secret",
|
||||
})
|
||||
|
||||
// $ExpectType Promise<string>
|
||||
JWTType.getToken({
|
||||
req: nextReq,
|
||||
raw: true,
|
||||
})
|
||||
|
||||
// $ExpectType Promise<WithAdditionalParams<JWT>>
|
||||
JWTType.getToken({
|
||||
req: nextReq,
|
||||
secret: "secret",
|
||||
})
|
||||
259
types/tests/providers.test.ts
Normal file
259
types/tests/providers.test.ts
Normal file
@@ -0,0 +1,259 @@
|
||||
import Providers from "next-auth/providers"
|
||||
|
||||
// $ExpectType NonNullParams<ProviderEmailOptions> & { id: "email"; type: "email"; }
|
||||
Providers.Email({
|
||||
server: "path/to/server",
|
||||
from: "path/from",
|
||||
})
|
||||
|
||||
// $ExpectType NonNullParams<ProviderEmailOptions> & { id: "email"; type: "email"; }
|
||||
Providers.Email({
|
||||
server: {
|
||||
host: "host",
|
||||
port: 123,
|
||||
auth: {
|
||||
user: "foo",
|
||||
pass: "123",
|
||||
},
|
||||
},
|
||||
from: "path/from",
|
||||
})
|
||||
|
||||
// $ExpectType NonNullParams<ProviderCredentialsOptions> & { id: "credentials"; type: "credentials"; }
|
||||
Providers.Credentials({
|
||||
id: "login",
|
||||
name: "account",
|
||||
credentials: {
|
||||
user: {
|
||||
label: "Password",
|
||||
type: "password",
|
||||
},
|
||||
password: {
|
||||
label: "Password",
|
||||
type: "password",
|
||||
},
|
||||
},
|
||||
authorize: async (credentials) => {
|
||||
const user = {
|
||||
/* fetched user */
|
||||
}
|
||||
return user
|
||||
},
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"apple", "oauth"> & { protection: "none"; }
|
||||
Providers.Apple({
|
||||
clientId: "foo123",
|
||||
clientSecret: {
|
||||
appleId: "foo@icloud.com",
|
||||
teamId: "foo",
|
||||
privateKey: "123xyz",
|
||||
keyId: "1234",
|
||||
},
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"twitter", "oauth">
|
||||
Providers.Twitter({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"facebook", "oauth">
|
||||
Providers.Facebook({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"github", "oauth">
|
||||
Providers.GitHub({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"github", "oauth">
|
||||
Providers.GitHub({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
scope: "change:thing read:that",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"gitlab", "oauth">
|
||||
Providers.GitLab({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"slack", "oauth">
|
||||
Providers.Slack({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"google", "oauth">
|
||||
Providers.Google({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"google", "oauth">
|
||||
Providers.Google({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
authorizationUrl: "https://foo.google.com",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"auth0", "oauth"> & { domain: string; }
|
||||
Providers.Auth0({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
domain: "https://foo.auth0.com",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"auth0", "oauth"> & { domain: string; }
|
||||
Providers.Auth0({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
domain: "https://foo.auth0.com",
|
||||
profile: () => ({
|
||||
id: "foo123",
|
||||
name: "foo",
|
||||
email: "foo@bar.io",
|
||||
image: "https://foo.auth0.com/image/1.png",
|
||||
}),
|
||||
})
|
||||
|
||||
// $ExpectType Provider<string, "oauth"> & { domain: string; }
|
||||
Providers.IdentityServer4({
|
||||
id: "identity-server4",
|
||||
name: "IdentityServer4",
|
||||
scope: "change:thing read:that",
|
||||
domain: "https://foo.is4.com",
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"discord", "oauth">
|
||||
Providers.Discord({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
scope: "identify",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"twitch", "oauth">
|
||||
Providers.Twitch({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"okta", "oauth"> & { domain: string; }
|
||||
Providers.Okta({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
domain: "https://foo.auth0.com",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"battlenet", "oauth"> & { region: string; }
|
||||
Providers.BattleNet({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
region: "europe",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"box", "oauth">
|
||||
Providers.Box({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"cognito", "oauth"> & { domain: string; }
|
||||
Providers.Cognito({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
domain: "https://foo.auth0.com",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"yandex", "oauth">
|
||||
Providers.Yandex({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"linkedin", "oauth">
|
||||
Providers.LinkedIn({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
scope: "r_emailaddress r_liteprofile",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"spotify", "oauth">
|
||||
Providers.Spotify({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"spotify", "oauth">
|
||||
Providers.Spotify({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
scope: "user-read-email",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"basecamp", "oauth">
|
||||
Providers.Basecamp({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"reddit", "oauth">
|
||||
Providers.Reddit({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"azure-ad-b2c", "oauth">
|
||||
Providers.AzureADB2C({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
scope: "offline_access User.Read",
|
||||
tenantId: "tenantId",
|
||||
idToken: true,
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"fusionauth", "oauth">
|
||||
Providers.FusionAuth({
|
||||
name: "FusionAuth",
|
||||
domain: "domain",
|
||||
clientId: "clientId",
|
||||
clientSecret: "clientSecret",
|
||||
tenantId: "tenantId",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"faceit", "oauth">
|
||||
Providers.FACEIT({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"instagram", "oauth">
|
||||
Providers.Instagram({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"kakao", "oauth">
|
||||
Providers.Kakao({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"osso", "oauth">
|
||||
Providers.Osso({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
|
||||
// $ExpectType Provider<"zoho", "oauth">
|
||||
Providers.Zoho({
|
||||
clientId: "foo123",
|
||||
clientSecret: "bar123",
|
||||
})
|
||||
255
types/tests/server.test.ts
Normal file
255
types/tests/server.test.ts
Normal file
@@ -0,0 +1,255 @@
|
||||
import Providers, { AppProvider, Provider } from "next-auth/providers"
|
||||
import Adapters, {
|
||||
Adapter,
|
||||
EmailAppProvider,
|
||||
Profile,
|
||||
Session,
|
||||
VerificationRequest,
|
||||
} from "next-auth/adapters"
|
||||
import NextAuth, * as NextAuthTypes from "next-auth"
|
||||
import { IncomingMessage, ServerResponse } from "http"
|
||||
import * as JWTType from "next-auth/jwt"
|
||||
import { Socket } from "net"
|
||||
import { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
const req: NextApiRequest = Object.assign(new IncomingMessage(new Socket()), {
|
||||
query: {},
|
||||
cookies: {},
|
||||
body: {},
|
||||
env: {},
|
||||
})
|
||||
|
||||
const res: NextApiResponse = Object.assign(new ServerResponse(req), {
|
||||
send: (body: string) => undefined,
|
||||
json: (body: string) => undefined,
|
||||
status: (code: number) => res,
|
||||
redirect: (statusOrUrl: number | string, url?: string) => res as any,
|
||||
setPreviewData: (data: object | string) => res,
|
||||
clearPreviewData: () => res,
|
||||
})
|
||||
|
||||
const pageOptions = {
|
||||
signin: "path/to/signin",
|
||||
signout: "path/to/signout",
|
||||
error: "path/to/error",
|
||||
verifyRequest: "path/to/verify",
|
||||
newUsers: "path/to/signup",
|
||||
}
|
||||
|
||||
const simpleConfig = {
|
||||
site: "https://foo.com",
|
||||
providers: [
|
||||
Providers.GitHub({
|
||||
clientId: "123",
|
||||
clientSecret: "123",
|
||||
scope:
|
||||
"user public_repo repo repo_deployment repo:status read:repo_hook read:org read:public_key read:gpg_key",
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
const exampleUser: NextAuthTypes.User = {
|
||||
name: "",
|
||||
image: "",
|
||||
email: "",
|
||||
}
|
||||
|
||||
const exampleSession: Session = {
|
||||
userId: "",
|
||||
accessToken: "",
|
||||
sessionToken: "",
|
||||
expires: new Date(),
|
||||
}
|
||||
|
||||
const exampleVerificatoinRequest: VerificationRequest = {
|
||||
identifier: "",
|
||||
token: "",
|
||||
expires: new Date(),
|
||||
}
|
||||
|
||||
const adapter: Adapter<
|
||||
NextAuthTypes.User,
|
||||
Profile,
|
||||
Session,
|
||||
VerificationRequest
|
||||
> = {
|
||||
async getAdapter(appOptions: NextAuthTypes.AppOptions) {
|
||||
return {
|
||||
createUser: async (profile: Profile) => exampleUser,
|
||||
getUser: async (id: string) => exampleUser,
|
||||
getUserByEmail: async (email: string) => exampleUser,
|
||||
getUserByProviderAccountId: async (
|
||||
providerId: string,
|
||||
providerAccountId: string
|
||||
) => exampleUser,
|
||||
updateUser: async (user: NextAuthTypes.User) => exampleUser,
|
||||
linkAccount: async (
|
||||
userId: string,
|
||||
providerId: string,
|
||||
providerType: string,
|
||||
providerAccountId: string,
|
||||
refreshToken: string,
|
||||
accessToken: string,
|
||||
accessTokenExpires: number
|
||||
) => undefined,
|
||||
createSession: async (user: NextAuthTypes.User) => exampleSession,
|
||||
getSession: async (sessionToken: string) => exampleSession,
|
||||
updateSession: async (session: Session, force?: boolean) =>
|
||||
exampleSession,
|
||||
deleteSession: async (sessionToken: string) => undefined,
|
||||
createVerificationRequest: async (
|
||||
email: string,
|
||||
url: string,
|
||||
token: string,
|
||||
secret: string,
|
||||
provider: EmailAppProvider,
|
||||
options: NextAuthTypes.AppOptions
|
||||
) => exampleVerificatoinRequest,
|
||||
getVerificationRequest: async (
|
||||
email: string,
|
||||
verificationToken: string,
|
||||
secret: string,
|
||||
provider: AppProvider
|
||||
) => exampleVerificatoinRequest,
|
||||
deleteVerificationRequest: async (
|
||||
email: string,
|
||||
verificationToken: string,
|
||||
secret: string,
|
||||
provider: AppProvider
|
||||
) => undefined,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const allConfig = {
|
||||
providers: [
|
||||
Providers.Twitter({
|
||||
clientId: "123",
|
||||
clientSecret: "123",
|
||||
}),
|
||||
],
|
||||
database: "path/to/db",
|
||||
debug: true,
|
||||
secret: "my secret",
|
||||
session: {
|
||||
jwt: true,
|
||||
maxAge: 365,
|
||||
updateAge: 60,
|
||||
},
|
||||
jwt: {
|
||||
secret: "secret-thing",
|
||||
maxAge: 365,
|
||||
encryption: true,
|
||||
signingKey: "some-key",
|
||||
encryptionKey: "some-key",
|
||||
encode: async () => "foo",
|
||||
decode: async () => ({}),
|
||||
},
|
||||
pages: pageOptions,
|
||||
callbacks: {
|
||||
async signIn(
|
||||
user: NextAuthTypes.User,
|
||||
account: Record<string, unknown>,
|
||||
profile: Record<string, unknown>
|
||||
) {
|
||||
return true
|
||||
},
|
||||
async redirect(url: string, baseUrl: string) {
|
||||
return "path/to/foo"
|
||||
},
|
||||
async session(
|
||||
session: NextAuthTypes.Session,
|
||||
userOrToken: NextAuthTypes.User
|
||||
) {
|
||||
return { ...session }
|
||||
},
|
||||
async jwt(
|
||||
token: JWTType.JWT,
|
||||
user?: NextAuthTypes.User,
|
||||
account?: Record<string, unknown>,
|
||||
profile?: Record<string, unknown>,
|
||||
isNewUser?: boolean
|
||||
) {
|
||||
return token
|
||||
},
|
||||
},
|
||||
events: {
|
||||
async signIn(message: string) {
|
||||
return undefined
|
||||
},
|
||||
async signOut(message: string) {
|
||||
return undefined
|
||||
},
|
||||
async createUser(message: string) {
|
||||
return undefined
|
||||
},
|
||||
async linkAccount(message: string) {
|
||||
return undefined
|
||||
},
|
||||
async session(message: string) {
|
||||
return undefined
|
||||
},
|
||||
async error(message: string) {
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
adapter,
|
||||
useSecureCookies: true,
|
||||
cookies: {
|
||||
sessionToken: {
|
||||
name: "__Secure-next-auth.session-token",
|
||||
options: {
|
||||
httpOnly: true,
|
||||
sameSite: true as true,
|
||||
path: "/",
|
||||
secure: true,
|
||||
domain: "foo.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const customProvider: Provider<"google"> = {
|
||||
id: "google",
|
||||
name: "Google",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope:
|
||||
"https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://accounts.google.com/o/oauth2/token",
|
||||
requestTokenUrl: "https://accounts.google.com/o/oauth2/auth",
|
||||
authorizationUrl:
|
||||
"https://accounts.google.com/o/oauth2/auth?response_type=code",
|
||||
profileUrl: "https://www.googleapis.com/oauth2/v1/userinfo?alt=json",
|
||||
async profile(profile, tokens) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.picture,
|
||||
}
|
||||
},
|
||||
clientId: "",
|
||||
clientSecret: "",
|
||||
}
|
||||
|
||||
const customProviderConfig = {
|
||||
site: "https://foo.com",
|
||||
providers: [customProvider],
|
||||
}
|
||||
|
||||
// $ExpectType void | Promise<void>
|
||||
NextAuth(simpleConfig)
|
||||
|
||||
// $ExpectType void | Promise<void>
|
||||
NextAuth(allConfig)
|
||||
|
||||
// $ExpectType void | Promise<void>
|
||||
NextAuth(customProviderConfig)
|
||||
|
||||
// $ExpectType void | Promise<void>
|
||||
NextAuth(req, res, simpleConfig)
|
||||
|
||||
// $ExpectType void | Promise<void>
|
||||
NextAuth(req, res, allConfig)
|
||||
13
types/tests/test-helpers.ts
Normal file
13
types/tests/test-helpers.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { IncomingMessage, ServerResponse } from "http"
|
||||
import { Socket } from "net"
|
||||
import { NextApiRequest } from "next"
|
||||
|
||||
export const nextReq: NextApiRequest = Object.assign(
|
||||
new IncomingMessage(new Socket()),
|
||||
{
|
||||
query: {},
|
||||
cookies: {},
|
||||
body: {},
|
||||
env: {},
|
||||
}
|
||||
)
|
||||
23
types/tsconfig.json
Normal file
23
types/tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"lib": ["es6", "dom"],
|
||||
"jsx": "react",
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictNullChecks": true,
|
||||
"esModuleInterop": true,
|
||||
"noEmit": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"next-auth": ["."],
|
||||
"next-auth/providers": ["./providers"],
|
||||
"next-auth/adapters": ["./adapters"],
|
||||
"next-auth/client": ["./client"],
|
||||
"next-auth/jwt": ["./jwt"],
|
||||
"next": ["./_next"]
|
||||
}
|
||||
}
|
||||
}
|
||||
6
types/tslint.json
Normal file
6
types/tslint.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "dtslint/dtslint.json",
|
||||
"rules": {
|
||||
"semicolon": false
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,17 @@ If your Next.js application uses a custom base path, specify the route to the AP
|
||||
_e.g. `NEXTAUTH_URL=https://example.com/custom-route/api/auth`_
|
||||
|
||||
:::tip
|
||||
To set environment variables on Vercel, you can use the [dashboard](https://vercel.com/dashboard) or the `now env` command.
|
||||
To set environment variables on Vercel, you can use the [dashboard](https://vercel.com/dashboard) or the `vercel env` command.
|
||||
:::
|
||||
|
||||
### NEXTAUTH_URL_INTERNAL
|
||||
|
||||
If provided, server-side calls will use this instead of `NEXTAUTH_URL`. Useful in environments when the server doesn't have access to the canonical URL of your site. Defaults to `NEXTAUTH_URL`.
|
||||
|
||||
```
|
||||
NEXTAUTH_URL_INTERNAL=http://10.240.8.16
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Options
|
||||
|
||||
@@ -42,11 +42,22 @@ export default function SignIn({ providers }) {
|
||||
)
|
||||
}
|
||||
|
||||
// This is the recommended way for Next.js 9.3 or newer
|
||||
export async function getServerSideProps(context){
|
||||
const providers = await providers()
|
||||
return {
|
||||
props: { providers }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// If older than Next.js 9.3
|
||||
SignIn.getInitialProps = async () => {
|
||||
return {
|
||||
providers: await providers()
|
||||
}
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
### Email Sign in
|
||||
@@ -54,7 +65,7 @@ SignIn.getInitialProps = async () => {
|
||||
If you create a custom sign in form for email sign in, you will need to submit both fields for the **email** address and **csrfToken** from **/api/auth/csrf** in a POST request to **/api/auth/signin/email**.
|
||||
|
||||
```jsx title="pages/auth/email-signin.js"
|
||||
import { csrfToken } from 'next-auth/client'
|
||||
import { getCsrfToken } from 'next-auth/client'
|
||||
|
||||
export default function SignIn({ csrfToken }) {
|
||||
return (
|
||||
@@ -62,18 +73,29 @@ export default function SignIn({ csrfToken }) {
|
||||
<input name='csrfToken' type='hidden' defaultValue={csrfToken}/>
|
||||
<label>
|
||||
Email address
|
||||
<input type='text' id='email' name='email'/>
|
||||
<input type='email' id='email' name='email'/>
|
||||
</label>
|
||||
<button type='submit'>Sign in with Email</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
SignIn.getInitialProps = async (context) => {
|
||||
// This is the recommended way for Next.js 9.3 or newer
|
||||
export async function getServerSideProps(context){
|
||||
const csrfToken = await getCsrfToken(context)
|
||||
return {
|
||||
csrfToken: await csrfToken(context)
|
||||
props: { csrfToken }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// If older than Next.js 9.3
|
||||
SignIn.getInitialProps = async (context) => {
|
||||
return {
|
||||
csrfToken: await getCsrfToken(context)
|
||||
}
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
You can also use the `signIn()` function which will handle obtaining the CSRF token for you:
|
||||
@@ -87,7 +109,7 @@ signIn('email', { email: 'jsmith@example.com' })
|
||||
If you create a sign in form for credentials based authentication, you will need to pass a **csrfToken** from **/api/auth/csrf** in a POST request to **/api/auth/callback/credentials**.
|
||||
|
||||
```jsx title="pages/auth/credentials-signin.js"
|
||||
import { csrfToken } from 'next-auth/client'
|
||||
import { getCsrfToken } from 'next-auth/client'
|
||||
|
||||
export default function SignIn({ csrfToken }) {
|
||||
return (
|
||||
@@ -99,18 +121,30 @@ export default function SignIn({ csrfToken }) {
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input name='password' type='text'/>
|
||||
<input name='password' type='password'/>
|
||||
</label>
|
||||
<button type='submit'>Sign in</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
SignIn.getInitialProps = async (context) => {
|
||||
// This is the recommended way for Next.js 9.3 or newer
|
||||
export async function getServerSideProps(context) {
|
||||
return {
|
||||
csrfToken: await csrfToken(context)
|
||||
props: {
|
||||
csrfToken: await getCsrfToken(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// If older than Next.js 9.3
|
||||
SignIn.getInitialProps = async (context) => {
|
||||
return {
|
||||
csrfToken: await getCsrfToken(context)
|
||||
}
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
You can also use the `signIn()` function which will handle obtaining the CSRF token for you:
|
||||
|
||||
@@ -56,15 +56,11 @@ NextAuth.js is designed to work with any OAuth service, it supports OAuth 1.0, 1
|
||||
|
||||
<Image src="/img/signin.png" alt="Signin Screenshot" />
|
||||
|
||||
:::tip
|
||||
If you want to create a custom sign in link you can link to **/api/auth/signin/[provider]** which will sign in the user in directly with that provider.
|
||||
:::
|
||||
|
||||
### Using a custom provider
|
||||
|
||||
You can use an OAuth provider that isn't built-in by using a custom object.
|
||||
|
||||
As an example of what this looks like, this is the the provider object returned for the Google provider:
|
||||
As an example of what this looks like, this is the provider object returned for the Google provider:
|
||||
|
||||
```js
|
||||
{
|
||||
|
||||
@@ -76,13 +76,31 @@ In _most cases_ it does not make sense to specify a database in NextAuth.js opti
|
||||
The provider you tried to use failed when setting [PKCE or Proof Key for Code Exchange](https://tools.ietf.org/html/rfc7636#section-4.2).
|
||||
The `code_verifier` is saved in a cookie called (by default) `__Secure-next-auth.pkce.code_verifier` which expires after 15 minutes.
|
||||
Check if `cookies.pkceCodeVerifier` is configured correctly. The default `code_challenge_method` is `"S256"`. This is currently not configurable to `"plain"`, as it is not recommended, and in most cases it is only supported for backward compatibility.
|
||||
|
||||
---
|
||||
|
||||
### Session Handling
|
||||
|
||||
#### JWT_SESSION_ERROR
|
||||
|
||||
https://next-auth.js.org/errors#jwt_session_error JWKKeySupport: the key does not support HS512 verify algorithm
|
||||
|
||||
The algorithm used for generating your key isn't listed as supported. You can generate a HS512 key using
|
||||
|
||||
````
|
||||
jose newkey -s 512 -t oct -a HS512
|
||||
````
|
||||
|
||||
If you are unable to use an HS512 key (for example to interoperate with other services) you can define what is supported using
|
||||
|
||||
````
|
||||
jwt: {
|
||||
signingKey: {"kty":"oct","kid":"--","alg":"HS256","k":"--"}
|
||||
verificationOptions: {
|
||||
algorithms: ["HS256"]
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
#### SESSION_ERROR
|
||||
|
||||
---
|
||||
@@ -139,4 +157,4 @@ Check your mail server configuration.
|
||||
|
||||
This error happens when `[...nextauth].js` file is not found inside `pages/api/auth`.
|
||||
|
||||
Make sure the file is there and the filename is written correctly.
|
||||
Make sure the file is there and the filename is written correctly.
|
||||
|
||||
@@ -356,7 +356,7 @@ export default function App ({ Component, pageProps }) {
|
||||
:::note
|
||||
**These options have no effect on clients that are not signed in.**
|
||||
|
||||
Every tab/window maintains it's own copy of the local session state; the session it is not stored in shared storage like localStorage or sessionStorage. Any update in one tab/window triggers a message to other tabs/windows to update their own session state.
|
||||
Every tab/window maintains its own copy of the local session state; the session is not stored in shared storage like localStorage or sessionStorage. Any update in one tab/window triggers a message to other tabs/windows to update their own session state.
|
||||
|
||||
Using low values for `clientMaxAge` or `keepAlive` will increase network traffic and load on authenticated clients and may impact hosting costs and performance.
|
||||
:::
|
||||
|
||||
@@ -35,7 +35,7 @@ The `POST` submission requires CSRF token from `/api/auth/csrf`.
|
||||
|
||||
Returns client-safe session object - or an empty object if there is no session.
|
||||
|
||||
The contents of the session object that is returned is configurable with the session callback.
|
||||
The contents of the session object that is returned are configurable with the session callback.
|
||||
|
||||
#### `GET` /api/auth/csrf
|
||||
|
||||
@@ -52,7 +52,7 @@ It can be used to dynamically generate custom sign up pages and to check what ca
|
||||
---
|
||||
|
||||
:::note
|
||||
The default base path is `/api/auth` but it is configurable by specyfing a custom path in `NEXTAUTH_URL`
|
||||
The default base path is `/api/auth` but it is configurable by specifying a custom path in `NEXTAUTH_URL`
|
||||
|
||||
e.g.
|
||||
|
||||
|
||||
@@ -1,22 +1,77 @@
|
||||
---
|
||||
id: typescript
|
||||
title: TypeScript Support
|
||||
title: TypeScript
|
||||
---
|
||||
|
||||
Currently, NextAuth.js relies on the community to provide TypeScript types. You can download it from [DefinitelyTyped](https://www.npmjs.com/package/@types/next-auth).
|
||||
NextAuth.js comes with its own types, so you can safely use it in your TypeScript projects. Even if you don't use TypeScript, IDEs like VSCode will pick this up, to provide you with a better developer experience. While you are typing, you will get suggestions of what certain objects are, and sometimes also links to documentation, and examples.
|
||||
|
||||
Add it to your project with:
|
||||
:::note
|
||||
The types at [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) under the name of `@types/next-auth` are now deprecated, and not maintained anymore.
|
||||
:::
|
||||
|
||||
```sh
|
||||
npm i -D @types/next-auth
|
||||
***
|
||||
## Module Augmentaion
|
||||
|
||||
`next-auth` comes with certain types/interfaces, that are shared across submodules. Good examples are `Session` and `JWT`. Ideally, you should only need to create these types at a single place, and TS should pick them up in every location where they are referenced. Luckily, this is exactly what Module Agumentation can do for us. Define your shared interfaces in a single location, and get type-safety across your application, when you use `next-auth` (or one of its submodules).
|
||||
|
||||
1. Let's look at `Session`:
|
||||
|
||||
```ts title="pages/api/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
|
||||
export default NextAuth({
|
||||
callbacks: {
|
||||
session(session, token) {
|
||||
return session // The type here should match the one returned in `useSession()`
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
or
|
||||
```ts title="pages/index.ts"
|
||||
import { useSession } from "next-auth/client"
|
||||
|
||||
```sh
|
||||
yarn add -D @types/next-auth
|
||||
export default function IndexPage() {
|
||||
// `session` should match `callbacks.session()` in `NextAuth()`
|
||||
const [session] = useSession()
|
||||
|
||||
return (
|
||||
// Your component
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
You can find an initial Pull Request at [next-auth#516](https://github.com/nextauthjs/next-auth/pull/516) adding TypeScript. At the time of this writing, it looks like we would like to go from a complete migration to a more relaxed, incremental rewrite.
|
||||
To extend/augment this type, create a `types/next-auth.d.ts` file in your project:
|
||||
|
||||
Feel free to open a Pull Request, if you would like to contribute!
|
||||
```ts title="types/next-auth.d.ts"
|
||||
import NextAuth from "next-auth"
|
||||
|
||||
declare module "next-auth" {
|
||||
interface Session {
|
||||
user: {
|
||||
/** The user's postal address. */
|
||||
address: string
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Make sure that the `types` folder is added to [`typeRoots`](https://www.typescriptlang.org/tsconfig/#typeRoots) in your project's `tsconfig.json` file.
|
||||
|
||||
2. Check out `JWT` also:
|
||||
|
||||
```ts title="types/next-auth.d.ts"
|
||||
declare module "next-auth/jwt" {
|
||||
interface JWT {
|
||||
/** OpenID ID Token */
|
||||
idToken?: string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that this time we declared `JWT` inside `next-auth/jwt`, as this is its default location.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions of any kind are always welcome, especially for TypeScript. Please keep in mind that we are a small team working on this project in our free time. We will try our best to give support, but if you think you have a solution for a problem, please open a PR!
|
||||
|
||||
@@ -91,7 +91,7 @@ providers: [
|
||||
Providers.Email({
|
||||
server: process.env.EMAIL_SERVER,
|
||||
from: process.env.EMAIL_FROM,
|
||||
sendVerificationRequest: ({ identifier: email, url, token, site, provider }) => { /* your function */ }
|
||||
sendVerificationRequest: ({ identifier: email, url, token, baseUrl, provider }) => { /* your function */ }
|
||||
})
|
||||
]
|
||||
```
|
||||
|
||||
30
www/docs/providers/faceit.md
Normal file
30
www/docs/providers/faceit.md
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
id: faceit
|
||||
title: FACEIT
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
https://cdn.faceit.com/third_party/docs/FACEIT_Connect_3.0.pdf
|
||||
|
||||
## Configuration
|
||||
|
||||
https://developers.faceit.com/apps
|
||||
|
||||
Grant type: `Authorization Code`
|
||||
|
||||
Scopes to have basic infos (email, nickname, guid and avatar) : `openid`, `email`, `profile`
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import Providers from `next-auth/providers`
|
||||
...
|
||||
providers: [
|
||||
Providers.FACEIT({
|
||||
clientId: process.env.FACEIT_CLIENT_ID,
|
||||
clientSecret: process.env.FACEIT_CLIENT_SECRET
|
||||
})
|
||||
]
|
||||
...
|
||||
```
|
||||
32
www/docs/providers/kakao.md
Normal file
32
www/docs/providers/kakao.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
id: kakao
|
||||
title: Kakao
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
https://developers.kakao.com/product/kakaoLogin
|
||||
|
||||
## Configuration
|
||||
|
||||
https://developers.kakao.com/docs/latest/en/kakaologin/common
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import Providers from `next-auth/providers`
|
||||
...
|
||||
providers: [
|
||||
Providers.Kakao({
|
||||
clientId: process.env.KAKAO_CLIENT_ID,
|
||||
clientSecret: process.env.KAKAO_CLIENT_SECRET
|
||||
})
|
||||
]
|
||||
...
|
||||
```
|
||||
|
||||
## Instructions
|
||||
|
||||
### Configuration
|
||||
|
||||
Create a provider and a Kakao application at `https://developers.kakao.com/console/app`. In the settings of the app under Kakao Login, activate web app, change consent items and configure callback URL.
|
||||
39
www/docs/providers/osso.md
Normal file
39
www/docs/providers/osso.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
id: osso
|
||||
title: Osso
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
Osso is an open source service that handles SAML authentication against Identity Providers, normalizes profiles, and makes those profiles available to you in an OAuth 2.0 code grant flow.
|
||||
|
||||
If you don't yet have an Osso instance, you can use [Osso's Demo App](https://demo.ossoapp.com) for your testing purposes. For documentation on deploying an Osso instance, see https://ossoapp.com/docs/deploy/overview/
|
||||
|
||||
## Configuration
|
||||
|
||||
You can configure your OAuth Clients on your Osso Admin UI, i.e. https://demo.ossoapp.com/admin/config - you'll need to get a Client ID and Secret and allow-list your redirect URIs.
|
||||
|
||||
[SAML SSO differs a bit from OAuth](https://ossoapp.com/blog/saml-vs-oauth) - for every tenant who wants to sign in to your application using SAML, you and your customer need to perform a multi-step configuration in Osso's Admin UI and the admin dashboard of the tenant's Identity Provider. Osso provides documentation for providers like Okta and OneLogin, cloud-based IDPs who also offer a developer account that's useful for testing. Osso also provides a [Mock IDP](https://idp.ossoapp.com) that you can use for testing without needing to sign up for an Identity Provider service.
|
||||
|
||||
See Osso's complete configuration and testing documentation at https://ossoapp.com/docs/configure/overview
|
||||
|
||||
## Example
|
||||
|
||||
A full example application is available at https://github.com/enterprise-oss/osso-next-auth-example and https://nextjs-demo.ossoapp.com
|
||||
|
||||
```js
|
||||
import Providers from `next-auth/providers`
|
||||
...
|
||||
providers: [
|
||||
Providers.Osso({
|
||||
clientId: process.env.OSSO_CLIENT_ID,
|
||||
clientSecret: process.env.OSSO_CLIENT_SECRET,
|
||||
domain: process.env.OSSO_DOMAIN
|
||||
})
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
:::note
|
||||
`domain` should be the fully qualified domain – e.g. `demo.ossoapp.com`
|
||||
:::
|
||||
26
www/docs/providers/zoho.md
Normal file
26
www/docs/providers/zoho.md
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
id: zoho
|
||||
title: Zoho
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
https://www.zoho.com/accounts/protocol/oauth/web-server-applications.html
|
||||
|
||||
## Configuration
|
||||
|
||||
https://api-console.zoho.com/
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import Providers from `next-auth/providers`
|
||||
...
|
||||
providers: [
|
||||
Providers.Zoho({
|
||||
clientId: process.env.ZOHO_CLIENT_ID,
|
||||
clientSecret: process.env.ZOHO_CLIENT_SECRET
|
||||
})
|
||||
]
|
||||
...
|
||||
```
|
||||
@@ -9,6 +9,14 @@ _These tutorials are contributed by the community and hosted on this site._
|
||||
|
||||
_New submissions and edits are welcome!_
|
||||
|
||||
### [NextJS Authentication Crash Course with NextAuth.js](https://youtu.be/o_wZIVmWteQ)
|
||||
|
||||
This tutorial dives in to the ins and outs of NextAuth including email, Github, Twitter and integrating with Auth0 in under hour.
|
||||
|
||||
### [Create your own NextAuth.js Login Pages](https://youtu.be/kB6YNYZ63fw)
|
||||
|
||||
This tutorial shows you how to jump in and create your own custom login pages versus using the ones provided by NextAuth.js
|
||||
|
||||
### [Refresh Token Rotation](tutorials/refresh-token-rotation)
|
||||
|
||||
How to implement refresh token rotation.
|
||||
|
||||
9895
www/package-lock.json
generated
9895
www/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user