mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c52ce57296 | ||
|
|
4dae822806 | ||
|
|
901f6fb189 | ||
|
|
bb2237d0f9 | ||
|
|
fab7ce8f94 | ||
|
|
2becdad990 | ||
|
|
e3c2c7756d | ||
|
|
718f2537cb |
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
@@ -2,14 +2,14 @@ name: Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'beta'
|
||||
- 'next'
|
||||
- '3.x'
|
||||
- "main"
|
||||
- "beta"
|
||||
- "next"
|
||||
- "3.x"
|
||||
pull_request:
|
||||
jobs:
|
||||
release:
|
||||
name: 'Release'
|
||||
name: "Release"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -20,7 +20,6 @@ jobs:
|
||||
node-version: 14
|
||||
- name: Install dependencies
|
||||
uses: bahmutov/npm-install@v1
|
||||
- run: npm run build
|
||||
- run: npx semantic-release@17
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
20
.gitignore
vendored
20
.gitignore
vendored
@@ -27,6 +27,24 @@ node_modules
|
||||
.cache-loader
|
||||
.next
|
||||
www/providers.json
|
||||
src/providers/index.js
|
||||
internals
|
||||
adapters.d.ts
|
||||
adapters.js
|
||||
client.d.ts
|
||||
client.js
|
||||
index.d.ts
|
||||
index.js
|
||||
jwt.d.ts
|
||||
jwt.js
|
||||
providers.d.ts
|
||||
providers.js
|
||||
|
||||
# Development app
|
||||
app/next-auth
|
||||
app/dist/css
|
||||
app/package-lock.json
|
||||
app/yarn.lock
|
||||
|
||||
# VS
|
||||
/.vs/slnx.sqlite-journal
|
||||
@@ -39,4 +57,4 @@ www/providers.json
|
||||
/_work
|
||||
|
||||
# Prisma migrations
|
||||
/prisma/migrations
|
||||
/prisma/migrations
|
||||
@@ -32,15 +32,15 @@ cd next-auth
|
||||
|
||||
2. Install packages:
|
||||
```sh
|
||||
npm i
|
||||
npm i && npm dev:setup
|
||||
```
|
||||
|
||||
3. Populate `.env.local`:
|
||||
|
||||
Copy `.env.local.example` to `.env.local`, and add your env variables for each provider you want to test.
|
||||
Copy `app/.env.local.example` to `app/.env.local`, and add your env variables for each provider you want to test.
|
||||
|
||||
> NOTE: You can add any environment variables to .env.local that you would like to use in your dev app.
|
||||
> You can find the next-auth config under`pages/api/auth/[...nextauth].js`.
|
||||
> You can find the next-auth config under`app/pages/api/auth/[...nextauth].js`.
|
||||
|
||||
1. Start the dev application/server:
|
||||
```sh
|
||||
@@ -57,11 +57,23 @@ If you need an example project to link to, you can use [next-auth-example](https
|
||||
|
||||
When running `npm run dev`, you start a Next.js dev server on `http://localhost:3000`, which includes hot reloading out of the box. Make changes on any of the files in `src` and see the changes immediately.
|
||||
|
||||
>NOTE: When working on CSS, you will need to manually refresh the page after changes. (Improving this through a PR is very welcome!)
|
||||
> NOTE: When working on CSS, you will have to manually refresh the page after changes. The reason for this is our pages using CSS are server-side rendered. (Improving this through a PR is very welcome!)
|
||||
|
||||
> NOTE: The setup is as follows: The development application lives inside the `app` folder, and whenever you make a change to the `src` folder in the root (where next-auth is), it gets copied into `app` every time (gitignored), so Next.js can pick them up and apply hot reloading. This is to avoid some annoying issues with how symlinks are working with different React builds, and also to provide a super-fast feedback loop while developing core features.
|
||||
|
||||
#### Providers
|
||||
|
||||
If you think your custom provider might be useful to others, we encourage you to open a PR and add it to the built-in list so others can discover it much more easily! You only need to add two changes:
|
||||
1. Add your config: [`src/providers/{provider}.js`](https://github.com/nextauthjs/next-auth/tree/main/src/providers) (Make sure you use a named default export, like `export default function YourProvider`!)
|
||||
2. Add provider documentation: [`www/docs/providers/{provider}.md`](https://github.com/nextauthjs/next-auth/tree/main/www/docs/providers)
|
||||
|
||||
That's it! 🎉 Others will be able to discover this provider much more easily now!
|
||||
|
||||
You can look at the existing built-in providers for inspiration.
|
||||
|
||||
#### Databases
|
||||
|
||||
Included is a Docker Compose file that starts up MySQL, Postgres, and MongoDB databases on localhost.
|
||||
Included is a Docker Compose file that starts up MySQL, PostgreSQL, and MongoDB databases on localhost.
|
||||
|
||||
It will use port `3306`, `5432`, and `27017` on localhost respectively; please make sure those ports are not used by other services on localhost.
|
||||
|
||||
|
||||
5
app/jsconfig.json
Normal file
5
app/jsconfig.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "."
|
||||
}
|
||||
}
|
||||
0
next-env.d.ts → app/next-env.d.ts
vendored
0
next-env.d.ts → app/next-env.d.ts
vendored
19
app/next.config.js
Normal file
19
app/next.config.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const path = require("path")
|
||||
|
||||
module.exports = {
|
||||
webpack(config) {
|
||||
config.resolve = {
|
||||
...config.resolve,
|
||||
alias: {
|
||||
...config.resolve.alias,
|
||||
"next-auth$": path.join(process.cwd(), "next-auth/server"),
|
||||
"next-auth/client$": path.join(process.cwd(), "next-auth/client"),
|
||||
"next-auth/jwt$": path.join(process.cwd(), "next-auth/lib/jwt"),
|
||||
"next-auth/adapters": path.join(process.cwd(), "next-auth/adapters"),
|
||||
"next-auth/providers": path.join(process.cwd(), "next-auth/providers"),
|
||||
},
|
||||
}
|
||||
|
||||
return config
|
||||
},
|
||||
}
|
||||
25
app/package.json
Normal file
25
app/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "next-auth-app",
|
||||
"version": "1.0.0",
|
||||
"description": "NextAuth.js Developer app",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm-run-all --parallel copy:app dev:css dev:next",
|
||||
"dev:next": "next dev",
|
||||
"copy:app": "cpx \"../src/**/*\" next-auth --watch",
|
||||
"copy:css": "cpx \"../dist/css/**/*\" dist/css --watch",
|
||||
"watch:css": "cd .. && npm run watch:css",
|
||||
"dev:css": "npm-run-all --parallel watch:css copy:css",
|
||||
"start": "next start"
|
||||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"next": "^10.1.3",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cpx": "^1.5.0",
|
||||
"npm-run-all": "^4.1.5"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Provider } from 'next-auth/client'
|
||||
import './styles.css'
|
||||
import { Provider } from "next-auth/client"
|
||||
import "./styles.css"
|
||||
|
||||
// Use the <Provider> to improve performance and allow components that call
|
||||
// `useSession()` anywhere in your application to access the `session` object.
|
||||
export default function App ({ Component, pageProps }) {
|
||||
export default function App({ Component, pageProps }) {
|
||||
return (
|
||||
<Provider
|
||||
// Provider options are not required but can be useful in situations where
|
||||
@@ -21,7 +21,7 @@ export default function App ({ Component, pageProps }) {
|
||||
//
|
||||
// Note: If a session has expired when keep alive is triggered, all open
|
||||
// windows / tabs will be updated to reflect the user is signed out.
|
||||
keepAlive: 0
|
||||
keepAlive: 0,
|
||||
}}
|
||||
session={pageProps.session}
|
||||
>
|
||||
@@ -9,6 +9,8 @@ const MODULE_ENTRIES = {
|
||||
JWT: "jwt",
|
||||
}
|
||||
|
||||
// Building submodule entries
|
||||
|
||||
const BUILD_TARGETS = {
|
||||
[`${MODULE_ENTRIES.SERVER}.js`]: "module.exports = require('./dist/server').default\n",
|
||||
[`${MODULE_ENTRIES.CLIENT}.js`]: "module.exports = require('./dist/client').default\n",
|
||||
@@ -24,6 +26,8 @@ Object.entries(BUILD_TARGETS).forEach(([target, content]) => {
|
||||
})
|
||||
})
|
||||
|
||||
// Building types
|
||||
|
||||
const TYPES_TARGETS = [
|
||||
`${MODULE_ENTRIES.SERVER}.d.ts`,
|
||||
`${MODULE_ENTRIES.CLIENT}.d.ts`,
|
||||
@@ -43,3 +47,40 @@ TYPES_TARGETS.forEach((target) => {
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// Building providers
|
||||
|
||||
const providersDir = path.join(process.cwd(), "/src/providers")
|
||||
|
||||
const files = fs.readdirSync(providersDir, "utf8")
|
||||
|
||||
let importLines = ""
|
||||
let exportLines = `export default {\n`
|
||||
files.forEach((file) => {
|
||||
const provider = fs.readFileSync(path.join(providersDir, file), "utf8")
|
||||
try {
|
||||
// NOTE: If this fails, the default export probably wasn't a named function.
|
||||
// Always use a named function as default export.
|
||||
// Eg.: export default function YourProvider ...
|
||||
const { functionName } = provider.match(
|
||||
/export default function (?<functionName>.+)\s?\(/
|
||||
).groups
|
||||
|
||||
importLines += `import ${functionName} from "./${file}"\n`
|
||||
exportLines += ` ${functionName},\n`
|
||||
} catch (error) {
|
||||
console.error(
|
||||
[
|
||||
`\nThe provider file '${file}' should have a single named default export`,
|
||||
"Example: 'export default function YourProvider'\n\n",
|
||||
].join("\n")
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
exportLines += `}\n`
|
||||
|
||||
fs.writeFile(
|
||||
path.join(process.cwd(), "src/providers/index.js"),
|
||||
[importLines, exportLines].join("\n")
|
||||
)
|
||||
|
||||
24
package.json
24
package.json
@@ -6,12 +6,22 @@
|
||||
"repository": "https://github.com/nextauthjs/next-auth.git",
|
||||
"author": "Iain Collins <me@iaincollins.com>",
|
||||
"main": "index.js",
|
||||
"types": "./index.d.ts",
|
||||
"keywords": ["react", "nodejs", "oauth", "jwt", "oauth2", "authentication", "nextjs", "csrf", "oidc", "nextauth"],
|
||||
"exports": {
|
||||
".": "./dist/server/index.js",
|
||||
"./jwt": "./dist/lib/jwt.js",
|
||||
"./adapters": "./dist/adapters/index.js",
|
||||
"./client": "./dist/client/index.js",
|
||||
"./providers": "./dist/providers/index.js",
|
||||
"./providers/*": "./dist/providers/*.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run build:js && npm run build:css",
|
||||
"build:js": "babel --config-file ./config/babel.config.json src --out-dir dist && node ./config/build.js",
|
||||
"build:js": "node ./config/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",
|
||||
"dev:with-css": "next | npm run watch:css",
|
||||
"dev": "next",
|
||||
"dev:setup": "npm run build:css && cd app && npm i",
|
||||
"dev": "cd app && npm run dev",
|
||||
"watch": "npm run watch:js | npm run watch:css",
|
||||
"watch:js": "babel --config-file ./config/babel.config.json --watch src --out-dir dist",
|
||||
"watch:css": "postcss --config config/postcss.config.js --watch src/**/*.css --base src --dir dist",
|
||||
@@ -143,6 +153,14 @@
|
||||
"fetch": "readonly"
|
||||
}
|
||||
},
|
||||
"release": {
|
||||
"branches": [
|
||||
"+([0-9])?(.{+([0-9]),x}).x",
|
||||
"main",
|
||||
{ "name": "beta", "prerelease": true },
|
||||
{ "name": "next", "prerelease": true }
|
||||
]
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
module.exports = {
|
||||
branches: [
|
||||
'+([0-9])?(.{+([0-9]),x}).x',
|
||||
'main',
|
||||
{ name: 'beta', prerelease: true },
|
||||
{ name: 'next', prerelease: true }
|
||||
]
|
||||
}
|
||||
@@ -1,26 +1,23 @@
|
||||
/** @type {import("types").LoggerInstance} */
|
||||
const _logger = {
|
||||
error (code, ...message) {
|
||||
error(code, ...message) {
|
||||
console.error(
|
||||
`[next-auth][error][${code.toLowerCase()}]`,
|
||||
`\nhttps://next-auth.js.org/errors#${code.toLowerCase()}`,
|
||||
...message
|
||||
)
|
||||
},
|
||||
warn (code, ...message) {
|
||||
warn(code, ...message) {
|
||||
console.warn(
|
||||
`[next-auth][warn][${code.toLowerCase()}]`,
|
||||
`\nhttps://next-auth.js.org/warnings#${code.toLowerCase()}`,
|
||||
...message
|
||||
)
|
||||
},
|
||||
debug (code, ...message) {
|
||||
debug(code, ...message) {
|
||||
if (!process?.env?._NEXTAUTH_DEBUG) return
|
||||
console.log(
|
||||
`[next-auth][debug][${code.toLowerCase()}]`,
|
||||
...message
|
||||
)
|
||||
}
|
||||
console.log(`[next-auth][debug][${code.toLowerCase()}]`, ...message)
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,7 +25,7 @@ const _logger = {
|
||||
* Any `undefined` level will use the default logger.
|
||||
* @param {Partial<import("types").LoggerInstance>} newLogger
|
||||
*/
|
||||
export function setLogger (newLogger = {}) {
|
||||
export function setLogger(newLogger = {}) {
|
||||
if (newLogger.error) _logger.error = newLogger.error
|
||||
if (newLogger.warn) _logger.warn = newLogger.warn
|
||||
if (newLogger.debug) _logger.debug = newLogger.debug
|
||||
@@ -42,13 +39,13 @@ export default _logger
|
||||
* @param {string} basePath
|
||||
* @return {import("types").LoggerInstance}
|
||||
*/
|
||||
export function proxyLogger (logger = _logger, basePath) {
|
||||
export function proxyLogger(logger = _logger, basePath) {
|
||||
try {
|
||||
if (typeof window === 'undefined') {
|
||||
if (typeof window === "undefined") {
|
||||
return logger
|
||||
}
|
||||
|
||||
const clientLogger = console
|
||||
const clientLogger = {}
|
||||
for (const level in logger) {
|
||||
clientLogger[level] = (code, ...message) => {
|
||||
_logger[level](code, ...message) // Log on client as usual
|
||||
@@ -57,21 +54,23 @@ export function proxyLogger (logger = _logger, basePath) {
|
||||
const body = new URLSearchParams({
|
||||
level,
|
||||
code,
|
||||
message: JSON.stringify(message.map(m => {
|
||||
if (m instanceof Error) {
|
||||
// Serializing errors: https://iaincollins.medium.com/error-handling-in-javascript-a6172ccdf9af
|
||||
return { name: m.name, message: m.message, stack: m.stack }
|
||||
}
|
||||
return m
|
||||
}))
|
||||
message: JSON.stringify(
|
||||
message.map((m) => {
|
||||
if (m instanceof Error) {
|
||||
// Serializing errors: https://iaincollins.medium.com/error-handling-in-javascript-a6172ccdf9af
|
||||
return { name: m.name, message: m.message, stack: m.stack }
|
||||
}
|
||||
return m
|
||||
})
|
||||
),
|
||||
})
|
||||
if (navigator.sendBeacon) {
|
||||
return navigator.sendBeacon(url, body)
|
||||
}
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
export default (options) => {
|
||||
export default function Apple(options) {
|
||||
return {
|
||||
id: 'apple',
|
||||
name: 'Apple',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'name email',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://appleid.apple.com/auth/token',
|
||||
authorizationUrl: 'https://appleid.apple.com/auth/authorize?response_type=code&id_token&response_mode=form_post',
|
||||
id: "apple",
|
||||
name: "Apple",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "name email",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://appleid.apple.com/auth/token",
|
||||
authorizationUrl:
|
||||
"https://appleid.apple.com/auth/authorize?response_type=code&id_token&response_mode=form_post",
|
||||
profileUrl: null,
|
||||
idToken: true,
|
||||
profile: (profile) => {
|
||||
profile(profile) {
|
||||
// The name of the user will only return on first login
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.user != null ? profile.user.name.firstName + ' ' + profile.user.name.lastName : null,
|
||||
email: profile.email
|
||||
name:
|
||||
profile.user != null
|
||||
? profile.user.name.firstName + " " + profile.user.name.lastName
|
||||
: null,
|
||||
email: profile.email,
|
||||
}
|
||||
},
|
||||
clientId: null,
|
||||
clientSecret: {
|
||||
teamId: null,
|
||||
privateKey: null,
|
||||
keyId: null
|
||||
keyId: null,
|
||||
},
|
||||
protection: 'none', // REVIEW: Apple does not support state, as far as I know. Can we use "pkce" then?
|
||||
...options
|
||||
protection: "none", // REVIEW: Apple does not support state, as far as I know. Can we use "pkce" then?
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
export default (options) => {
|
||||
export default function Atlassian(options) {
|
||||
return {
|
||||
id: 'atlassian',
|
||||
name: 'Atlassian',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
id: "atlassian",
|
||||
name: "Atlassian",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
params: {
|
||||
grant_type: 'authorization_code'
|
||||
grant_type: "authorization_code",
|
||||
},
|
||||
accessTokenUrl: 'https://auth.atlassian.com/oauth/token',
|
||||
accessTokenUrl: "https://auth.atlassian.com/oauth/token",
|
||||
authorizationUrl:
|
||||
'https://auth.atlassian.com/authorize?audience=api.atlassian.com&response_type=code&prompt=consent',
|
||||
profileUrl: 'https://api.atlassian.com/me',
|
||||
profile: (profile) => {
|
||||
"https://auth.atlassian.com/authorize?audience=api.atlassian.com&response_type=code&prompt=consent",
|
||||
profileUrl: "https://api.atlassian.com/me",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.account_id,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.picture
|
||||
image: profile.picture,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
export default (options) => {
|
||||
export default function Auth0(options) {
|
||||
return {
|
||||
id: 'auth0',
|
||||
name: 'Auth0',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
scope: 'openid email profile',
|
||||
id: "auth0",
|
||||
name: "Auth0",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
params: { grant_type: "authorization_code" },
|
||||
scope: "openid email profile",
|
||||
accessTokenUrl: `https://${options.domain}/oauth/token`,
|
||||
authorizationUrl: `https://${options.domain}/authorize?response_type=code`,
|
||||
profileUrl: `https://${options.domain}/userinfo`,
|
||||
profile: (profile) => {
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.nickname,
|
||||
email: profile.email,
|
||||
image: profile.picture
|
||||
image: profile.picture,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
export default (options) => {
|
||||
const tenant = options.tenantId ? options.tenantId : 'common'
|
||||
export default function AzureADB2C(options) {
|
||||
const tenant = options.tenantId ? options.tenantId : "common"
|
||||
|
||||
return {
|
||||
id: 'azure-ad-b2c',
|
||||
name: 'Azure Active Directory B2C',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
id: "azure-ad-b2c",
|
||||
name: "Azure Active Directory B2C",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
params: {
|
||||
grant_type: 'authorization_code'
|
||||
grant_type: "authorization_code",
|
||||
},
|
||||
accessTokenUrl: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`,
|
||||
authorizationUrl: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/authorize?response_type=code&response_mode=query`,
|
||||
profileUrl: 'https://graph.microsoft.com/v1.0/me/',
|
||||
profile: (profile) => {
|
||||
profileUrl: "https://graph.microsoft.com/v1.0/me/",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.displayName,
|
||||
email: profile.userPrincipalName
|
||||
email: profile.userPrincipalName,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
export default (options) => {
|
||||
export default function Basecamp(options) {
|
||||
return {
|
||||
id: 'basecamp',
|
||||
name: 'Basecamp',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
accessTokenUrl: 'https://launchpad.37signals.com/authorization/token?type=web_server',
|
||||
authorizationUrl: 'https://launchpad.37signals.com/authorization/new?type=web_server',
|
||||
profileUrl: 'https://launchpad.37signals.com/authorization.json',
|
||||
profile: (profile) => {
|
||||
id: "basecamp",
|
||||
name: "Basecamp",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
accessTokenUrl:
|
||||
"https://launchpad.37signals.com/authorization/token?type=web_server",
|
||||
authorizationUrl:
|
||||
"https://launchpad.37signals.com/authorization/new?type=web_server",
|
||||
profileUrl: "https://launchpad.37signals.com/authorization.json",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.identity.id,
|
||||
name: `${profile.identity.first_name} ${profile.identity.last_name}`,
|
||||
email: profile.identity.email_address,
|
||||
image: null
|
||||
image: null,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
export default (options) => {
|
||||
export default function BattleNet(options) {
|
||||
const { region } = options
|
||||
return {
|
||||
id: 'battlenet',
|
||||
name: 'Battle.net',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'openid',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
id: "battlenet",
|
||||
name: "Battle.net",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "openid",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl:
|
||||
region === 'CN'
|
||||
? 'https://www.battlenet.com.cn/oauth/token'
|
||||
region === "CN"
|
||||
? "https://www.battlenet.com.cn/oauth/token"
|
||||
: `https://${region}.battle.net/oauth/token`,
|
||||
authorizationUrl:
|
||||
region === 'CN'
|
||||
? 'https://www.battlenet.com.cn/oauth/authorize?response_type=code'
|
||||
region === "CN"
|
||||
? "https://www.battlenet.com.cn/oauth/authorize?response_type=code"
|
||||
: `https://${region}.battle.net/oauth/authorize?response_type=code`,
|
||||
profileUrl: 'https://us.battle.net/oauth/userinfo',
|
||||
profile: (profile) => {
|
||||
profileUrl: "https://us.battle.net/oauth/userinfo",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.battletag,
|
||||
email: null,
|
||||
image: null
|
||||
image: null,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
export default (options) => {
|
||||
export default function Box(options) {
|
||||
return {
|
||||
id: 'box',
|
||||
name: 'Box',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: '',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://api.box.com/oauth2/token',
|
||||
authorizationUrl: 'https://account.box.com/api/oauth2/authorize?response_type=code',
|
||||
profileUrl: 'https://api.box.com/2.0/users/me',
|
||||
profile: (profile) => {
|
||||
id: "box",
|
||||
name: "Box",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://api.box.com/oauth2/token",
|
||||
authorizationUrl:
|
||||
"https://account.box.com/api/oauth2/authorize?response_type=code",
|
||||
profileUrl: "https://api.box.com/2.0/users/me",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.name,
|
||||
email: profile.login,
|
||||
image: profile.avatar_url
|
||||
image: profile.avatar_url,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
export default (options) => {
|
||||
export default function Bungie(options) {
|
||||
return {
|
||||
id: 'bungie',
|
||||
name: 'Bungie',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: '',
|
||||
params: { reauth: 'true', grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://www.bungie.net/platform/app/oauth/token/',
|
||||
requestTokenUrl: 'https://www.bungie.net/platform/app/oauth/token/',
|
||||
authorizationUrl: 'https://www.bungie.net/en/OAuth/Authorize?response_type=code',
|
||||
profileUrl: 'https://www.bungie.net/platform/User/GetBungieAccount/{membershipId}/254/',
|
||||
profile: (profile) => {
|
||||
id: "bungie",
|
||||
name: "Bungie",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "",
|
||||
params: { reauth: "true", grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://www.bungie.net/platform/app/oauth/token/",
|
||||
requestTokenUrl: "https://www.bungie.net/platform/app/oauth/token/",
|
||||
authorizationUrl:
|
||||
"https://www.bungie.net/en/OAuth/Authorize?response_type=code",
|
||||
profileUrl:
|
||||
"https://www.bungie.net/platform/User/GetBungieAccount/{membershipId}/254/",
|
||||
profile(profile) {
|
||||
const { bungieNetUser: user } = profile.Response
|
||||
|
||||
return {
|
||||
id: user.membershipId,
|
||||
name: user.displayName,
|
||||
image: `https://www.bungie.net${user.profilePicturePath.startsWith('/') ? '' : '/'}${user.profilePicturePath}`,
|
||||
email: null
|
||||
image: `https://www.bungie.net${
|
||||
user.profilePicturePath.startsWith("/") ? "" : "/"
|
||||
}${user.profilePicturePath}`,
|
||||
email: null,
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
'X-API-Key': null
|
||||
"X-API-Key": null,
|
||||
},
|
||||
clientId: null,
|
||||
clientSecret: null,
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
export default (options) => {
|
||||
export default function Cognito(options) {
|
||||
const { domain } = options
|
||||
return {
|
||||
id: 'cognito',
|
||||
name: 'Cognito',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'openid profile email',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
id: "cognito",
|
||||
name: "Cognito",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "openid profile email",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: `https://${domain}/oauth2/token`,
|
||||
authorizationUrl: `https://${domain}/oauth2/authorize?response_type=code`,
|
||||
profileUrl: `https://${domain}/oauth2/userInfo`,
|
||||
profile: (profile) => {
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.username,
|
||||
email: profile.email,
|
||||
image: null
|
||||
image: null,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export default (options) => {
|
||||
export default function Credentials(options) {
|
||||
return {
|
||||
id: 'credentials',
|
||||
name: 'Credentials',
|
||||
type: 'credentials',
|
||||
id: "credentials",
|
||||
name: "Credentials",
|
||||
type: "credentials",
|
||||
authorize: null,
|
||||
credentials: null,
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
export default (options) => {
|
||||
export default function Discord(options) {
|
||||
return {
|
||||
id: 'discord',
|
||||
name: 'Discord',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'identify email',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://discord.com/api/oauth2/token',
|
||||
authorizationUrl: 'https://discord.com/api/oauth2/authorize?response_type=code&prompt=none',
|
||||
profileUrl: 'https://discord.com/api/users/@me',
|
||||
profile: (profile) => {
|
||||
id: "discord",
|
||||
name: "Discord",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "identify email",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://discord.com/api/oauth2/token",
|
||||
authorizationUrl:
|
||||
"https://discord.com/api/oauth2/authorize?response_type=code&prompt=none",
|
||||
profileUrl: "https://discord.com/api/users/@me",
|
||||
profile(profile) {
|
||||
if (profile.avatar === null) {
|
||||
const defaultAvatarNumber = parseInt(profile.discriminator) % 5
|
||||
profile.image_url = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarNumber}.png`
|
||||
} else {
|
||||
const format = profile.avatar.startsWith('a_') ? 'gif' : 'png'
|
||||
const format = profile.avatar.startsWith("a_") ? "gif" : "png"
|
||||
profile.image_url = `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.${format}`
|
||||
}
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.username,
|
||||
image: profile.image_url,
|
||||
email: profile.email
|
||||
email: profile.email,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,54 @@
|
||||
import nodemailer from 'nodemailer'
|
||||
import logger from '../lib/logger'
|
||||
import nodemailer from "nodemailer"
|
||||
import logger from "../lib/logger"
|
||||
|
||||
export default (options) => {
|
||||
export default function Email(options) {
|
||||
return {
|
||||
id: 'email',
|
||||
type: 'email',
|
||||
name: 'Email',
|
||||
id: "email",
|
||||
type: "email",
|
||||
name: "Email",
|
||||
// Server can be an SMTP connection string or a nodemailer config object
|
||||
server: {
|
||||
host: 'localhost',
|
||||
host: "localhost",
|
||||
port: 25,
|
||||
auth: {
|
||||
user: '',
|
||||
pass: ''
|
||||
}
|
||||
user: "",
|
||||
pass: "",
|
||||
},
|
||||
},
|
||||
from: 'NextAuth <no-reply@example.com>',
|
||||
maxAge: 24 * 60 * 60, // How long email links are valid for (default 24h)
|
||||
from: "NextAuth <no-reply@example.com>",
|
||||
maxAge: 24 * 60 * 60,
|
||||
sendVerificationRequest,
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
const sendVerificationRequest = ({ identifier: email, url, baseUrl, provider }) => {
|
||||
const sendVerificationRequest = ({
|
||||
identifier: email,
|
||||
url,
|
||||
baseUrl,
|
||||
provider,
|
||||
}) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { server, from } = provider
|
||||
// Strip protocol from URL and use domain as site name
|
||||
const site = baseUrl.replace(/^https?:\/\//, '')
|
||||
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()
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -52,16 +58,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, '​.')}`
|
||||
const escapedSite = `${site.replace(/\./g, '​.')}`
|
||||
const escapedEmail = `${email.replace(/\./g, "​.")}`
|
||||
const escapedSite = `${site.replace(/\./g, "​.")}`
|
||||
|
||||
// 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"
|
||||
|
||||
return `
|
||||
<body style="background: ${backgroundColor};">
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
export default (options) => {
|
||||
export default function EVEOnline(options) {
|
||||
return {
|
||||
id: 'eveonline',
|
||||
name: 'EVE Online',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://login.eveonline.com/oauth/token',
|
||||
authorizationUrl: 'https://login.eveonline.com/oauth/authorize?response_type=code',
|
||||
profileUrl: 'https://login.eveonline.com/oauth/verify',
|
||||
profile: (profile) => {
|
||||
id: "eveonline",
|
||||
name: "EVE Online",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://login.eveonline.com/oauth/token",
|
||||
authorizationUrl:
|
||||
"https://login.eveonline.com/oauth/authorize?response_type=code",
|
||||
profileUrl: "https://login.eveonline.com/oauth/verify",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.CharacterID,
|
||||
name: profile.CharacterName,
|
||||
image: `https://image.eveonline.com/Character/${profile.CharacterID}_128.jpg`,
|
||||
email: null
|
||||
email: null,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
export default (options) => {
|
||||
export default function Facebook(options) {
|
||||
return {
|
||||
id: 'facebook',
|
||||
name: 'Facebook',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'email',
|
||||
accessTokenUrl: 'https://graph.facebook.com/oauth/access_token',
|
||||
authorizationUrl: 'https://www.facebook.com/v7.0/dialog/oauth?response_type=code',
|
||||
profileUrl: 'https://graph.facebook.com/me?fields=email,name,picture',
|
||||
profile: (profile) => {
|
||||
id: "facebook",
|
||||
name: "Facebook",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "email",
|
||||
accessTokenUrl: "https://graph.facebook.com/oauth/access_token",
|
||||
authorizationUrl:
|
||||
"https://www.facebook.com/v7.0/dialog/oauth?response_type=code",
|
||||
profileUrl: "https://graph.facebook.com/me?fields=email,name,picture",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.picture.data.url
|
||||
image: profile.picture.data.url,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
export default (options) => {
|
||||
export default function FACEIT(options) {
|
||||
return {
|
||||
id: 'faceit',
|
||||
name: 'FACEIT',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
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')}`
|
||||
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) {
|
||||
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
|
||||
image,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
export default ({ apiVersion, ...options }) => {
|
||||
export default function Foursquare(options) {
|
||||
const { apiVersion } = options
|
||||
return {
|
||||
id: 'foursquare',
|
||||
name: 'Foursquare',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://foursquare.com/oauth2/access_token',
|
||||
id: "foursquare",
|
||||
name: "Foursquare",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://foursquare.com/oauth2/access_token",
|
||||
authorizationUrl:
|
||||
'https://foursquare.com/oauth2/authenticate?response_type=code',
|
||||
"https://foursquare.com/oauth2/authenticate?response_type=code",
|
||||
profileUrl: `https://api.foursquare.com/v2/users/self?v=${apiVersion}`,
|
||||
profile: (profile) => {
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: `${profile.firstName} ${profile.lastName}`,
|
||||
image: `${profile.prefix}original${profile.suffix}`,
|
||||
email: profile.contact.email
|
||||
email: profile.contact.email,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
export default (options) => {
|
||||
export default function FusionAuth(options) {
|
||||
let authorizationUrl = `https://${options.domain}/oauth2/authorize?response_type=code`
|
||||
if (options.tenantId) {
|
||||
authorizationUrl += `&tenantId=${options.tenantId}`
|
||||
}
|
||||
|
||||
return {
|
||||
id: 'fusionauth',
|
||||
name: 'FusionAuth',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'openid',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
id: "fusionauth",
|
||||
name: "FusionAuth",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "openid",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: `https://${options.domain}/oauth2/token`,
|
||||
authorizationUrl,
|
||||
profileUrl: `https://${options.domain}/oauth2/userinfo`,
|
||||
profile: (profile) => {
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.picture
|
||||
image: profile.picture,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
export default (options) => {
|
||||
export default function GitHub(options) {
|
||||
return {
|
||||
id: 'github',
|
||||
name: 'GitHub',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'user',
|
||||
accessTokenUrl: 'https://github.com/login/oauth/access_token',
|
||||
authorizationUrl: 'https://github.com/login/oauth/authorize',
|
||||
profileUrl: 'https://api.github.com/user',
|
||||
profile: (profile) => {
|
||||
id: "github",
|
||||
name: "GitHub",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "user",
|
||||
accessTokenUrl: "https://github.com/login/oauth/access_token",
|
||||
authorizationUrl: "https://github.com/login/oauth/authorize",
|
||||
profileUrl: "https://api.github.com/user",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.name || profile.login,
|
||||
email: profile.email,
|
||||
image: profile.avatar_url
|
||||
image: profile.avatar_url,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
export default (options) => {
|
||||
export default function GitLab(options) {
|
||||
return {
|
||||
id: 'gitlab',
|
||||
name: 'GitLab',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'read_user',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://gitlab.com/oauth/token',
|
||||
authorizationUrl: 'https://gitlab.com/oauth/authorize?response_type=code',
|
||||
profileUrl: 'https://gitlab.com/api/v4/user',
|
||||
profile: (profile) => {
|
||||
id: "gitlab",
|
||||
name: "GitLab",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "read_user",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://gitlab.com/oauth/token",
|
||||
authorizationUrl: "https://gitlab.com/oauth/authorize?response_type=code",
|
||||
profileUrl: "https://gitlab.com/api/v4/user",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.username,
|
||||
email: profile.email,
|
||||
image: profile.avatar_url
|
||||
image: profile.avatar_url,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
export default (options) => {
|
||||
export default function Google(options) {
|
||||
return {
|
||||
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',
|
||||
profile: (profile) => {
|
||||
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",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.picture
|
||||
image: profile.picture,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
export default (options) => {
|
||||
export default function IdentityServer4(options) {
|
||||
return {
|
||||
id: 'identity-server4',
|
||||
name: 'IdentityServer4',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'openid profile email',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
id: "identity-server4",
|
||||
name: "IdentityServer4",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "openid profile email",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: `https://${options.domain}/connect/token`,
|
||||
authorizationUrl: `https://${options.domain}/connect/authorize?response_type=code`,
|
||||
profileUrl: `https://${options.domain}/connect/userinfo`,
|
||||
profile: (profile) => {
|
||||
profile(profile) {
|
||||
return { ...profile, id: profile.sub }
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
import Apple from './apple'
|
||||
import Atlassian from './atlassian'
|
||||
import Auth0 from './auth0'
|
||||
import AzureADB2C from './azure-ad-b2c'
|
||||
import Basecamp from './basecamp'
|
||||
import BattleNet from './battlenet'
|
||||
import Box from './box'
|
||||
import Bungie from './bungie'
|
||||
import Cognito from './cognito'
|
||||
import Credentials from './credentials'
|
||||
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'
|
||||
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'
|
||||
import Spotify from './spotify'
|
||||
import Strava from './strava'
|
||||
import Twitch from './twitch'
|
||||
import Twitter from './twitter'
|
||||
import VK from './vk'
|
||||
import Yandex from './yandex'
|
||||
import Zoho from './zoho'
|
||||
|
||||
export default {
|
||||
Apple,
|
||||
Atlassian,
|
||||
Auth0,
|
||||
AzureADB2C,
|
||||
Basecamp,
|
||||
BattleNet,
|
||||
Box,
|
||||
Bungie,
|
||||
Cognito,
|
||||
Credentials,
|
||||
Discord,
|
||||
Email,
|
||||
EVEOnline,
|
||||
Facebook,
|
||||
FACEIT,
|
||||
Foursquare,
|
||||
FusionAuth,
|
||||
GitHub,
|
||||
GitLab,
|
||||
Google,
|
||||
IdentityServer4,
|
||||
Instagram,
|
||||
Kakao,
|
||||
LINE,
|
||||
LinkedIn,
|
||||
MailRu,
|
||||
Medium,
|
||||
Netlify,
|
||||
Okta,
|
||||
Osso,
|
||||
Reddit,
|
||||
Salesforce,
|
||||
Slack,
|
||||
Spotify,
|
||||
Strava,
|
||||
Twitch,
|
||||
Twitter,
|
||||
VK,
|
||||
Yandex,
|
||||
Zoho
|
||||
}
|
||||
@@ -1,21 +1,22 @@
|
||||
export default (options) => {
|
||||
export default function Kakao(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) => {
|
||||
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
|
||||
image: profile.kakao_account?.profile.profile_image_url,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
export default (options) => {
|
||||
export default function LINE(options) {
|
||||
return {
|
||||
id: 'line',
|
||||
name: 'LINE',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'profile openid',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://api.line.me/oauth2/v2.1/token',
|
||||
authorizationUrl: 'https://access.line.me/oauth2/v2.1/authorize?response_type=code',
|
||||
profileUrl: 'https://api.line.me/v2/profile',
|
||||
profile: (profile) => {
|
||||
id: "line",
|
||||
name: "LINE",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "profile openid",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://api.line.me/oauth2/v2.1/token",
|
||||
authorizationUrl:
|
||||
"https://access.line.me/oauth2/v2.1/authorize?response_type=code",
|
||||
profileUrl: "https://api.line.me/v2/profile",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.userId,
|
||||
name: profile.displayName,
|
||||
email: null,
|
||||
image: profile.pictureUrl
|
||||
image: profile.pictureUrl,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
export default (options) => {
|
||||
export default function LinkedIn(options) {
|
||||
return {
|
||||
id: 'linkedin',
|
||||
name: 'LinkedIn',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'r_liteprofile',
|
||||
id: "linkedin",
|
||||
name: "LinkedIn",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "r_liteprofile",
|
||||
params: {
|
||||
grant_type: 'authorization_code',
|
||||
grant_type: "authorization_code",
|
||||
client_id: options.clientId,
|
||||
client_secret: options.clientSecret
|
||||
client_secret: options.clientSecret,
|
||||
},
|
||||
accessTokenUrl: 'https://www.linkedin.com/oauth/v2/accessToken',
|
||||
authorizationUrl: 'https://www.linkedin.com/oauth/v2/authorization?response_type=code',
|
||||
profileUrl: 'https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName)',
|
||||
profile: (profile) => {
|
||||
accessTokenUrl: "https://www.linkedin.com/oauth/v2/accessToken",
|
||||
authorizationUrl:
|
||||
"https://www.linkedin.com/oauth/v2/authorization?response_type=code",
|
||||
profileUrl:
|
||||
"https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName)",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.localizedFirstName + ' ' + profile.localizedLastName,
|
||||
name: profile.localizedFirstName + " " + profile.localizedLastName,
|
||||
email: null,
|
||||
image: null
|
||||
image: null,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
export default (options) => {
|
||||
export default function MailRu(options) {
|
||||
return {
|
||||
id: 'mailru',
|
||||
name: 'Mail.ru',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'userinfo',
|
||||
id: "mailru",
|
||||
name: "Mail.ru",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "userinfo",
|
||||
params: {
|
||||
grant_type: 'authorization_code'
|
||||
grant_type: "authorization_code",
|
||||
},
|
||||
accessTokenUrl: 'https://oauth.mail.ru/token',
|
||||
requestTokenUrl: 'https://oauth.mail.ru/token',
|
||||
authorizationUrl: 'https://oauth.mail.ru/login?response_type=code',
|
||||
profileUrl: 'https://oauth.mail.ru/userinfo',
|
||||
profile: (profile) => {
|
||||
accessTokenUrl: "https://oauth.mail.ru/token",
|
||||
requestTokenUrl: "https://oauth.mail.ru/token",
|
||||
authorizationUrl: "https://oauth.mail.ru/login?response_type=code",
|
||||
profileUrl: "https://oauth.mail.ru/userinfo",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.image
|
||||
image: profile.image,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
export default (options) => {
|
||||
export default function Medium(options) {
|
||||
return {
|
||||
id: 'medium',
|
||||
name: 'Medium',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'basicProfile',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://api.medium.com/v1/tokens',
|
||||
authorizationUrl: 'https://medium.com/m/oauth/authorize?response_type=code',
|
||||
profileUrl: 'https://api.medium.com/v1/me',
|
||||
profile: (profile) => {
|
||||
id: "medium",
|
||||
name: "Medium",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "basicProfile",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://api.medium.com/v1/tokens",
|
||||
authorizationUrl: "https://medium.com/m/oauth/authorize?response_type=code",
|
||||
profileUrl: "https://api.medium.com/v1/me",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.data.id,
|
||||
name: profile.data.name,
|
||||
email: null,
|
||||
image: profile.data.imageUrl
|
||||
image: profile.data.imageUrl,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
export default (options) => {
|
||||
export default function Netlify(options) {
|
||||
return {
|
||||
id: 'netlify',
|
||||
name: 'Netlify',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://api.netlify.com/oauth/token',
|
||||
authorizationUrl: 'https://app.netlify.com/authorize?response_type=code',
|
||||
profileUrl: 'https://api.netlify.com/api/v1/user',
|
||||
profile: (profile) => {
|
||||
id: "netlify",
|
||||
name: "Netlify",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://api.netlify.com/oauth/token",
|
||||
authorizationUrl: "https://app.netlify.com/authorize?response_type=code",
|
||||
profileUrl: "https://api.netlify.com/api/v1/user",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.full_name,
|
||||
email: profile.email,
|
||||
image: profile.avatar_url
|
||||
image: profile.avatar_url,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
export default (options) => {
|
||||
export default function Okta(options) {
|
||||
return {
|
||||
id: 'okta',
|
||||
name: 'Okta',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'openid profile email',
|
||||
id: "okta",
|
||||
name: "Okta",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "openid profile email",
|
||||
params: {
|
||||
grant_type: 'authorization_code',
|
||||
grant_type: "authorization_code",
|
||||
client_id: options.clientId,
|
||||
client_secret: options.clientSecret
|
||||
client_secret: options.clientSecret,
|
||||
},
|
||||
// These will be different depending on the Org.
|
||||
accessTokenUrl: `https://${options.domain}/v1/token`,
|
||||
authorizationUrl: `https://${options.domain}/v1/authorize/?response_type=code`,
|
||||
profileUrl: `https://${options.domain}/v1/userinfo/`,
|
||||
profile: (profile) => {
|
||||
profile(profile) {
|
||||
return { ...profile, id: profile.sub }
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
export default (options) => {
|
||||
export default function Osso(options) {
|
||||
return {
|
||||
id: 'osso',
|
||||
name: 'SAML SSO',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
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) => {
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.name || profile.email,
|
||||
email: profile.email
|
||||
email: profile.email,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
export default (options) => {
|
||||
export default function Reddit(options) {
|
||||
return {
|
||||
id: 'reddit',
|
||||
name: 'Reddit',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'identity',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: ' https://www.reddit.com/api/v1/access_token',
|
||||
id: "reddit",
|
||||
name: "Reddit",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "identity",
|
||||
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',
|
||||
profileUrl: 'https://oauth.reddit.com/api/v1/me',
|
||||
profile: (profile) => {
|
||||
"https://www.reddit.com/api/v1/authorize?response_type=code",
|
||||
profileUrl: "https://oauth.reddit.com/api/v1/me",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.name,
|
||||
image: null,
|
||||
email: null
|
||||
email: null,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
export default (options) => {
|
||||
export default function Salesforce(options) {
|
||||
return {
|
||||
id: 'salesforce',
|
||||
name: 'Salesforce',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
params: { display: 'page', grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://login.salesforce.com/services/oauth2/token',
|
||||
authorizationUrl: 'https://login.salesforce.com/services/oauth2/authorize?response_type=code',
|
||||
profileUrl: 'https://login.salesforce.com/services/oauth2/userinfo',
|
||||
protection: 'none', // REVIEW: Can we use "pkce" ?
|
||||
profile: (profile) => {
|
||||
id: "salesforce",
|
||||
name: "Salesforce",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
params: { display: "page", grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://login.salesforce.com/services/oauth2/token",
|
||||
authorizationUrl:
|
||||
"https://login.salesforce.com/services/oauth2/authorize?response_type=code",
|
||||
profileUrl: "https://login.salesforce.com/services/oauth2/userinfo",
|
||||
protection: "none",
|
||||
profile(profile) {
|
||||
return {
|
||||
...profile,
|
||||
id: profile.user_id,
|
||||
image: profile.picture
|
||||
image: profile.picture,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
export default (options) => {
|
||||
export default function Slack(options) {
|
||||
return {
|
||||
id: 'slack',
|
||||
name: 'Slack',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
id: "slack",
|
||||
name: "Slack",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: [],
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://slack.com/api/oauth.v2.access',
|
||||
authorizationUrl: 'https://slack.com/oauth/v2/authorize',
|
||||
authorizationParams: { user_scope: 'identity.basic,identity.email,identity.avatar' },
|
||||
profileUrl: 'https://slack.com/api/users.identity',
|
||||
profile: (profile) => {
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://slack.com/api/oauth.v2.access",
|
||||
authorizationUrl: "https://slack.com/oauth/v2/authorize",
|
||||
authorizationParams: {
|
||||
user_scope: "identity.basic,identity.email,identity.avatar",
|
||||
},
|
||||
profileUrl: "https://slack.com/api/users.identity",
|
||||
profile(profile) {
|
||||
const { user } = profile
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
image: user.image_512,
|
||||
email: user.email
|
||||
email: user.email,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
export default (options) => {
|
||||
export default function Spotify(options) {
|
||||
return {
|
||||
id: 'spotify',
|
||||
name: 'Spotify',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'user-read-email',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://accounts.spotify.com/api/token',
|
||||
id: "spotify",
|
||||
name: "Spotify",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "user-read-email",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://accounts.spotify.com/api/token",
|
||||
authorizationUrl:
|
||||
'https://accounts.spotify.com/authorize?response_type=code',
|
||||
profileUrl: 'https://api.spotify.com/v1/me',
|
||||
profile: (profile) => {
|
||||
"https://accounts.spotify.com/authorize?response_type=code",
|
||||
profileUrl: "https://api.spotify.com/v1/me",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.display_name,
|
||||
email: profile.email,
|
||||
image: profile.images?.[0]?.url
|
||||
image: profile.images?.[0]?.url,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
export default (options) => {
|
||||
export default function Strava(options) {
|
||||
return {
|
||||
id: 'strava',
|
||||
name: 'Strava',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'read',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://www.strava.com/api/v3/oauth/token',
|
||||
id: "strava",
|
||||
name: "Strava",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "read",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://www.strava.com/api/v3/oauth/token",
|
||||
authorizationUrl:
|
||||
'https://www.strava.com/api/v3/oauth/authorize?response_type=code',
|
||||
profileUrl: 'https://www.strava.com/api/v3/athlete',
|
||||
profile: (profile) => {
|
||||
"https://www.strava.com/api/v3/oauth/authorize?response_type=code",
|
||||
profileUrl: "https://www.strava.com/api/v3/athlete",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.firstname,
|
||||
image: profile.profile
|
||||
image: profile.profile,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
export default (options) => {
|
||||
export default function Twitch(options) {
|
||||
return {
|
||||
id: 'twitch',
|
||||
name: 'Twitch',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'user:read:email',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://id.twitch.tv/oauth2/token',
|
||||
id: "twitch",
|
||||
name: "Twitch",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "user:read:email",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://id.twitch.tv/oauth2/token",
|
||||
authorizationUrl:
|
||||
'https://id.twitch.tv/oauth2/authorize?response_type=code',
|
||||
profileUrl: 'https://api.twitch.tv/helix/users',
|
||||
profile: (profile) => {
|
||||
"https://id.twitch.tv/oauth2/authorize?response_type=code",
|
||||
profileUrl: "https://api.twitch.tv/helix/users",
|
||||
profile(profile) {
|
||||
const data = profile.data[0]
|
||||
return {
|
||||
id: data.id,
|
||||
name: data.display_name,
|
||||
image: data.profile_image_url,
|
||||
email: data.email
|
||||
email: data.email,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
export default (options) => {
|
||||
export default function Twitter(options) {
|
||||
return {
|
||||
id: 'twitter',
|
||||
name: 'Twitter',
|
||||
type: 'oauth',
|
||||
version: '1.0A',
|
||||
scope: '',
|
||||
accessTokenUrl: 'https://api.twitter.com/oauth/access_token',
|
||||
requestTokenUrl: 'https://api.twitter.com/oauth/request_token',
|
||||
authorizationUrl: 'https://api.twitter.com/oauth/authenticate',
|
||||
id: "twitter",
|
||||
name: "Twitter",
|
||||
type: "oauth",
|
||||
version: "1.0A",
|
||||
scope: "",
|
||||
accessTokenUrl: "https://api.twitter.com/oauth/access_token",
|
||||
requestTokenUrl: "https://api.twitter.com/oauth/request_token",
|
||||
authorizationUrl: "https://api.twitter.com/oauth/authenticate",
|
||||
profileUrl:
|
||||
'https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true',
|
||||
profile: (profile) => {
|
||||
"https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id_str,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.profile_image_url_https.replace(/_normal\.jpg$/, '.jpg')
|
||||
image: profile.profile_image_url_https.replace(/_normal\.jpg$/, ".jpg"),
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
export default (options) => {
|
||||
const apiVersion = '5.126' // https://vk.com/dev/versions
|
||||
export default function VK(options) {
|
||||
const apiVersion = "5.126" // https://vk.com/dev/versions
|
||||
|
||||
return {
|
||||
id: 'vk',
|
||||
name: 'VK',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'email',
|
||||
id: "vk",
|
||||
name: "VK",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "email",
|
||||
params: {
|
||||
grant_type: 'authorization_code'
|
||||
grant_type: "authorization_code",
|
||||
},
|
||||
accessTokenUrl: `https://oauth.vk.com/access_token?v=${apiVersion}`,
|
||||
requestTokenUrl: `https://oauth.vk.com/access_token?v=${apiVersion}`,
|
||||
authorizationUrl:
|
||||
`https://oauth.vk.com/authorize?response_type=code&v=${apiVersion}`,
|
||||
authorizationUrl: `https://oauth.vk.com/authorize?response_type=code&v=${apiVersion}`,
|
||||
profileUrl: `https://api.vk.com/method/users.get?fields=photo_100&v=${apiVersion}`,
|
||||
profile: (result) => {
|
||||
const profile = result.response?.[0] ?? {}
|
||||
|
||||
return {
|
||||
id: profile.id,
|
||||
name: [profile.first_name, profile.last_name].filter(Boolean).join(' '),
|
||||
name: [profile.first_name, profile.last_name].filter(Boolean).join(" "),
|
||||
email: profile.email,
|
||||
image: profile.photo_100
|
||||
image: profile.photo_100,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
export default (options) => {
|
||||
export default function Yandex(options) {
|
||||
return {
|
||||
id: 'yandex',
|
||||
name: 'Yandex',
|
||||
type: 'oauth',
|
||||
version: '2.0',
|
||||
scope: 'login:email login:info',
|
||||
params: { grant_type: 'authorization_code' },
|
||||
accessTokenUrl: 'https://oauth.yandex.ru/token',
|
||||
requestTokenUrl: 'https://oauth.yandex.ru/token',
|
||||
authorizationUrl: 'https://oauth.yandex.ru/authorize?response_type=code',
|
||||
profileUrl: 'https://login.yandex.ru/info?format=json',
|
||||
profile: (profile) => {
|
||||
id: "yandex",
|
||||
name: "Yandex",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "login:email login:info",
|
||||
params: { grant_type: "authorization_code" },
|
||||
accessTokenUrl: "https://oauth.yandex.ru/token",
|
||||
requestTokenUrl: "https://oauth.yandex.ru/token",
|
||||
authorizationUrl: "https://oauth.yandex.ru/authorize?response_type=code",
|
||||
profileUrl: "https://login.yandex.ru/info?format=json",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.real_name,
|
||||
email: profile.default_email,
|
||||
image: null
|
||||
image: null,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
export default (options) => {
|
||||
export default function Zoho(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) => {
|
||||
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
|
||||
image: null,
|
||||
}
|
||||
},
|
||||
...options
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
95
src/server/index.d.ts
vendored
95
src/server/index.d.ts
vendored
@@ -1,95 +0,0 @@
|
||||
import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
|
||||
import { LoggerInstance } from 'src/lib/logger'
|
||||
import { CallbacksOptions } from './lib/callbacks'
|
||||
import { CookiesOptions } from './lib/cookie'
|
||||
import { EventsOptions } from './lib/events'
|
||||
|
||||
export interface Provider {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
version: string
|
||||
params: Record<string, unknown>
|
||||
scope: string
|
||||
accessTokenUrl: string
|
||||
authorizationUrl: string
|
||||
profileUrl?: string
|
||||
grant_type?: string
|
||||
profile?: (profile: any) => Promise<any>
|
||||
}
|
||||
|
||||
/** @docs https://next-auth.js.org/configuration/options */
|
||||
export interface NextAuthOptions {
|
||||
/** @docs https://next-auth.js.org/configuration/options#theme */
|
||||
theme?: 'auto' | 'dark' | 'light'
|
||||
/** @docs https://next-auth.js.org/configuration/options#providers */
|
||||
providers: Provider[]
|
||||
/** @docs https://next-auth.js.org/configuration/options#database */
|
||||
database?: any
|
||||
/** @docs https://next-auth.js.org/configuration/options#secret */
|
||||
secret?: any
|
||||
/** @docs https://next-auth.js.org/configuration/options#session */
|
||||
session?: any
|
||||
/** @docs https://next-auth.js.org/configuration/options#jwt */
|
||||
jwt?: any
|
||||
/** @docs https://next-auth.js.org/configuration/options#pages */
|
||||
pages?: {
|
||||
signIn?: string
|
||||
signOut?: string
|
||||
/** Error code passed in query string as ?error= */
|
||||
error?: string
|
||||
verifyRequest?: string
|
||||
/** If set, new users will be directed here on first sign in */
|
||||
newUser?: string
|
||||
}
|
||||
/**
|
||||
* Callbacks are asynchronous functions you can use to control what happens when an action is performed.
|
||||
* Callbacks are extremely powerful, especially in scenarios involving JSON Web Tokens as
|
||||
* they allow you to implement access controls without a database and
|
||||
* to integrate with external databases or APIs.
|
||||
* @docs https://next-auth.js.org/configuration/options#callbacks
|
||||
*/
|
||||
callbacks?: CallbacksOptions
|
||||
/** @docs https://next-auth.js.org/configuration/options#events */
|
||||
events?: EventsOptions
|
||||
/** @docs https://next-auth.js.org/configuration/options#adapter */
|
||||
adapter?: any
|
||||
/** @docs https://next-auth.js.org/configuration/options#debug */
|
||||
debug?: boolean
|
||||
/** @docs https://next-auth.js.org/configuration/options#usesecurecookies */
|
||||
useSecureCookies?: boolean
|
||||
/** @docs https://next-auth.js.org/configuration/options#cookies */
|
||||
cookies?: CookiesOptions
|
||||
/** @docs https://next-auth.js.org/configuration/options#logger */
|
||||
logger: LoggerInstance
|
||||
}
|
||||
|
||||
/** Options that are the same both in internal and user provided options. */
|
||||
export type NextAuthSharedOptions = 'pages' | 'jwt' | 'events' | 'callbacks' | 'cookies' | 'secret' | 'adapter' | 'theme' | 'debug' | 'logger'
|
||||
|
||||
export interface NextAuthInternalOptions extends Pick<NextAuthOptions, NextAuthSharedOptions> {
|
||||
pkce?: {
|
||||
code_verifier?: string
|
||||
/**
|
||||
* Could be `"plain"`, but not recommended.
|
||||
* We ignore it for now.
|
||||
* @spec https://tools.ietf.org/html/rfc7636#section-4.2.
|
||||
*/
|
||||
code_challenge_method?: 'S256'
|
||||
}
|
||||
provider?: Provider
|
||||
baseUrl?: string
|
||||
basePath?: string
|
||||
action?: string
|
||||
csrfToken?: string
|
||||
csrfTokenVerified?: boolean
|
||||
}
|
||||
|
||||
export interface NextAuthRequest extends NextApiRequest {
|
||||
options: NextAuthInternalOptions
|
||||
}
|
||||
|
||||
export interface NextAuthResponse extends NextApiResponse {}
|
||||
|
||||
export declare function NextAuthHandler (req: NextAuthRequest, res: NextAuthResponse, options: NextAuthOptions): ReturnType<NextApiHandler>
|
||||
export declare function NextAuthHandler (options: NextAuthOptions): ReturnType<NextApiHandler>
|
||||
@@ -111,14 +111,7 @@ providers: [
|
||||
...
|
||||
```
|
||||
|
||||
:::tip
|
||||
If you think your custom provider might be useful to others, we encourage you to open a PR and add it to the built-in list so others can discover it much more easily! You only need to add three changes:
|
||||
1. Add your config: [`src/providers/{provider}.js`](https://github.com/nextauthjs/next-auth/tree/main/src/providers)
|
||||
2. Re-export your config: at [`src/providers/index.js`](https://github.com/nextauthjs/next-auth/blob/main/src/providers/index.js)
|
||||
3. Add provider documentation: [`www/docs/providers/{provider}.md`](https://github.com/nextauthjs/next-auth/tree/main/www/docs/providers)
|
||||
|
||||
You can look at the existing built-in providers for inspiration.
|
||||
:::
|
||||
|
||||
|
||||
### OAuth provider options
|
||||
@@ -230,3 +223,14 @@ export const Image = ({ children, src, alt = '' }) => (
|
||||
<img alt={alt} src={src} />
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
## Adding a new built-in provider
|
||||
|
||||
If you think your custom provider might be useful to others, we encourage you to open a PR and add it to the built-in list so others can discover it much more easily! You only need to add two changes:
|
||||
1. Add your config: [`src/providers/{provider}.js`](https://github.com/nextauthjs/next-auth/tree/main/src/providers) (Make sure you use a named default export, like `export default function YourProvider`!)
|
||||
2. Add provider documentation: [`www/docs/providers/{provider}.md`](https://github.com/nextauthjs/next-auth/tree/main/www/docs/providers)
|
||||
|
||||
That's it! 🎉 Others will be able to discover this provider much more easily now!
|
||||
|
||||
You can look at the existing built-in providers for inspiration.
|
||||
@@ -5,16 +5,21 @@ title: TypeScript
|
||||
|
||||
NextAuth.js comes with its own type definitions, 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 about how certain objects/functions look like, and sometimes also links to documentation, examples and other useful resources.
|
||||
|
||||
Check out the example repository showcasing how to use `next-auth` on a Next.js application with TypeScript:
|
||||
https://github.com/nextauthjs/next-auth-typescript-example
|
||||
|
||||
:::warning
|
||||
The types at [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) under the name of `@types/next-auth` are now deprecated, and not maintained anymore.
|
||||
The types at [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) under the name of `@types/next-auth` are now deprecated, and not maintained anymore.
|
||||
:::
|
||||
|
||||
***
|
||||
---
|
||||
|
||||
## Module Augmentation
|
||||
|
||||
`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 Augmentation 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).
|
||||
|
||||
### Main module
|
||||
|
||||
Let's look at `Session`:
|
||||
|
||||
```ts title="pages/api/[...nextauth].ts"
|
||||
@@ -24,8 +29,8 @@ export default NextAuth({
|
||||
callbacks: {
|
||||
session(session, token) {
|
||||
return session // The type here should match the one returned in `useSession()`
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -34,7 +39,7 @@ import { useSession } from "next-auth/client"
|
||||
|
||||
export default function IndexPage() {
|
||||
// `session` should match `callbacks.session()` in `NextAuth()`
|
||||
const [session] = useSession()
|
||||
const [session] = useSession()
|
||||
|
||||
return (
|
||||
// Your component
|
||||
@@ -59,33 +64,36 @@ declare module "next-auth" {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Popular interfaces to augment
|
||||
|
||||
Although you can augment almost anything, here are some of the more common interfaces that you might want to override in the `next-auth` module:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* The shape of the user object returned in the OAuth providers' `profile` callback,
|
||||
* or the second parameter of the `session` callback, when using a database.
|
||||
*/
|
||||
interface User {}
|
||||
/**
|
||||
* Usually contains information about the provider being used
|
||||
* and also extends `TokenSet`, which is different tokens returned by OAuth Providers.
|
||||
*/
|
||||
interface Account {}
|
||||
/** The OAuth profile returned from your provider */
|
||||
interface Profile {}
|
||||
/**
|
||||
* The shape of the user object returned in the OAuth providers' `profile` callback,
|
||||
* or the second parameter of the `session` callback, when using a database.
|
||||
*/
|
||||
interface User {}
|
||||
/**
|
||||
* Usually contains information about the provider being used
|
||||
* and also extends `TokenSet`, which is different tokens returned by OAuth Providers.
|
||||
*/
|
||||
interface Account {}
|
||||
/** The OAuth profile returned from your provider */
|
||||
interface Profile {}
|
||||
```
|
||||
|
||||
Make sure that the `types` folder is added to [`typeRoots`](https://www.typescriptlang.org/tsconfig/#typeRoots) in your project's `tsconfig.json` file.
|
||||
|
||||
### Submodules
|
||||
|
||||
The `JWT` interface can be found in the `next-auth/jwt` submodule:
|
||||
|
||||
```ts title="types/next-auth.d.ts"
|
||||
declare module "next-auth/jwt" {
|
||||
/** Returned by the `jwt` callback and `getToken`, when using JWT sessions */
|
||||
interface JWT {
|
||||
interface JWT {
|
||||
/** OpenID ID Token */
|
||||
idToken?: string
|
||||
}
|
||||
@@ -93,6 +101,7 @@ declare module "next-auth/jwt" {
|
||||
```
|
||||
|
||||
### Useful links
|
||||
|
||||
1. [TypeScript documentation: Module Augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation)
|
||||
2. [Digital Ocean: Module Augmentation in TypeScript](https://www.digitalocean.com/community/tutorials/typescript-module-augmentation)
|
||||
|
||||
@@ -102,4 +111,4 @@ Contributions of any kind are always welcome, especially for TypeScript. Please
|
||||
|
||||
:::note
|
||||
When contributing to TypeScript, if the actual JavaScript user API does not change in a breaking manner, we reserve the right to push any TypeScript change in a minor release. This is to ensure that we can keep us on a faster release cycle.
|
||||
:::
|
||||
:::
|
||||
|
||||
@@ -43,6 +43,10 @@ This approach can be used to authenticate existing user accounts against any bac
|
||||
|
||||
How to write tests using Cypress.
|
||||
|
||||
### [Usage with class components](tutorials/usage-with-class-components)
|
||||
|
||||
How to use `useSession()` hook with class components.
|
||||
|
||||
## Other tutorials and explainers
|
||||
|
||||
_These are tutorials and explainers that have been submitted or that we have found on the web and are hosted elsewhere They include articles, videos and example projects. Submissions for inclusion are welcome!_
|
||||
|
||||
61
www/docs/tutorials/usage-with-class-components.md
Normal file
61
www/docs/tutorials/usage-with-class-components.md
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
id: usage-with-class-components
|
||||
title: Usage with class components
|
||||
---
|
||||
|
||||
If you want to use the `useSession()` hook in your class components you can do so with the help of a higher order component or with a render prop.
|
||||
|
||||
## Higher Order Component
|
||||
|
||||
```js
|
||||
import { useSession } from "next-auth/client"
|
||||
|
||||
const withSession = Component => props => {
|
||||
const [session, loading] = useSession()
|
||||
|
||||
// if the component has a render property, we are good
|
||||
if (Component.prototype.render) {
|
||||
return <Component session={session} loading={loading} {...props} />
|
||||
}
|
||||
|
||||
// if the passed component is a function component, there is no need for this wrapper
|
||||
throw new Error([
|
||||
"You passed a function component, `withSession` is not needed.",
|
||||
"You can `useSession` directly in your component."
|
||||
].join("\n"))
|
||||
};
|
||||
|
||||
// Usage
|
||||
class ClassComponent extends React.Component {
|
||||
render() {
|
||||
const {session, loading} = this.props
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const ClassComponentWithSession = withSession(ClassComponent)
|
||||
```
|
||||
|
||||
## Render Prop
|
||||
|
||||
```js
|
||||
import { useSession } from "next-auth/client"
|
||||
|
||||
const UseSession = ({ children }) => {
|
||||
const [session, loading] = useSession()
|
||||
return children({ session, loading })
|
||||
};
|
||||
|
||||
// Usage
|
||||
class ClassComponent extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<UseSession>
|
||||
{({ session, loading }) => (
|
||||
<pre>{JSON.stringify(session, null, 2)}</pre>
|
||||
)}
|
||||
</UseSession>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user