mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59985264a2 | ||
|
|
c844296982 | ||
|
|
d1aa2a1a8e | ||
|
|
8139126f29 | ||
|
|
aa0e8200b3 | ||
|
|
82447f8e3e | ||
|
|
a0b3814c81 | ||
|
|
90c7d535c0 | ||
|
|
0510c9b1ba | ||
|
|
49e4af17e2 | ||
|
|
db65afe5ab | ||
|
|
36ca1f99e3 | ||
|
|
9bec96784f | ||
|
|
227ff2259f | ||
|
|
c71cb8457d | ||
|
|
a09a75be53 | ||
|
|
c4936991e5 | ||
|
|
e2add6a597 | ||
|
|
0e8be0c7d2 | ||
|
|
d1d2d977fe | ||
|
|
48749d7320 | ||
|
|
87d0beb70c | ||
|
|
978e2eeb08 | ||
|
|
8ab057ea33 | ||
|
|
2c269a6a81 | ||
|
|
8b9a109255 | ||
|
|
ac35d9f739 | ||
|
|
30a0fc6bc0 | ||
|
|
b0f6175cec | ||
|
|
1c7fe57edb | ||
|
|
59797bbdef | ||
|
|
9eb78a9de9 | ||
|
|
2670bbb28f | ||
|
|
0431c2a334 | ||
|
|
5ac688cc18 | ||
|
|
8ea75f0c1c |
@@ -54,7 +54,7 @@ See [next-auth.js.org](https://next-auth.js.org) for more information and docume
|
||||
### Flexible and easy to use
|
||||
|
||||
- Designed to work with any OAuth service, it supports OAuth 1.0, 1.0A and 2.0
|
||||
- Built-in support for [many popular sign-in services](https://next-auth.js.org/configuration/providers)
|
||||
- Built-in support for [many popular sign-in services](https://next-auth.js.org/providers/overview)
|
||||
- Supports email / passwordless authentication
|
||||
- Supports stateless authentication with any backend (Active Directory, LDAP, etc)
|
||||
- Supports both JSON Web Tokens and database sessions
|
||||
@@ -149,7 +149,7 @@ export default function Component() {
|
||||
|
||||
### Share/configure session state
|
||||
|
||||
Use the `<SessionProvider>` to allows instances of `useSession()` to share the session object across components. It also takes care of keeping the session updated and synced between tabs/windows.
|
||||
Use the `<SessionProvider>` to allow instances of `useSession()` to share the session object across components. It also takes care of keeping the session updated and synced between tabs/windows.
|
||||
|
||||
```jsx title="pages/_app.js"
|
||||
import { SessionProvider } from "next-auth/react"
|
||||
@@ -179,7 +179,7 @@ export default function App({
|
||||
|
||||
### Support
|
||||
|
||||
We're happy to announce we've recently created an [OpenCollective](https://opencollective.org/nextauth) for individuals and companies looking to contribute financially to the project!
|
||||
We're happy to announce we've recently created an [OpenCollective](https://opencollective.com/nextauth) for individuals and companies looking to contribute financially to the project!
|
||||
|
||||
<!--sponsors start-->
|
||||
<table>
|
||||
@@ -229,7 +229,7 @@ We're happy to announce we've recently created an [OpenCollective](https://openc
|
||||
## Contributing
|
||||
|
||||
We're open to all community contributions! If you'd like to contribute in any way, please first read
|
||||
our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/canary/CONTRIBUTING.md).
|
||||
our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
16
SECURITY.md
16
SECURITY.md
@@ -2,12 +2,6 @@
|
||||
|
||||
NextAuth.js practices responsible disclosure.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Security updates are only released for the current version.
|
||||
|
||||
Old releases are not maintained and do not receive updates.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We request that you contact us directly to report serious issues that might impact the security of sites using NextAuth.js.
|
||||
@@ -19,6 +13,12 @@ If you contact us regarding a serious issue:
|
||||
- We will disclose the issue (and credit you, with your consent) once a fix to resolve the issue has been released.
|
||||
- If 90 days has elapsed and we still don't have a fix, we will disclose the issue publicly.
|
||||
|
||||
Currently, the best way to report an issue is by contacting us via email at me@iaincollins.com or info@balazsorban.com and yo@ndo.dev.
|
||||
The best way to report an issue is by contacting us via email at info@balazsorban.com or me@iaincollins.com and yo@ndo.dev, or raise a public issue requesting someone get in touch with you via whatever means you prefer for more details. (Please do not disclose sensitive details publicly at this stage.)
|
||||
|
||||
For less serious issues (e.g. RFC compliance for unsupported flows or potential issues that may cause a problem future or default behaviour / options) it is appropriate to submit these these publically as bug reports or feature requests or to raise a question to open a discussion around them.
|
||||
> For less serious issues (e.g. RFC compliance for unsupported flows or potential issues that may cause a problem in the future) it is appropriate to submit these these publically as bug reports or feature requests or to raise a question to open a discussion around them.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Security updates are only released for the current version.
|
||||
|
||||
Old releases are not maintained and do not receive updates.
|
||||
|
||||
1
app/next-env.d.ts
vendored
1
app/next-env.d.ts
vendored
@@ -1,5 +1,4 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
|
||||
@@ -3,6 +3,7 @@ const path = require("path")
|
||||
module.exports = {
|
||||
webpack(config) {
|
||||
config.experiments = {
|
||||
...config.experiments,
|
||||
topLevelAwait: true,
|
||||
}
|
||||
config.resolve = {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"clean": "rm -rf .next",
|
||||
"postinstall": "rm -rf node_modules/next-auth",
|
||||
"dev": "npm-run-all --parallel dev:next watch:css copy:css ",
|
||||
"dev:next": "next dev",
|
||||
"build": "next build",
|
||||
@@ -12,23 +13,23 @@
|
||||
"watch:css": "cd .. && npm run watch:css",
|
||||
"start": "next start",
|
||||
"email": "npx fake-smtp-server",
|
||||
"start:email": "email"
|
||||
"start:email": "npm run email"
|
||||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@next-auth/fauna-adapter": "0.2.2-next.4",
|
||||
"@next-auth/prisma-adapter": "0.5.2-next.5",
|
||||
"@prisma/client": "^2.29.1",
|
||||
"@next-auth/fauna-adapter": "^1.0.1",
|
||||
"@next-auth/prisma-adapter": "^1.0.1",
|
||||
"@prisma/client": "^3.7.0",
|
||||
"fake-smtp-server": "^0.8.0",
|
||||
"faunadb": "^4.3.0",
|
||||
"next": "^11.1.0",
|
||||
"nodemailer": "^6.6.3",
|
||||
"faunadb": "^4.4.1",
|
||||
"next": "^12.0.7",
|
||||
"nodemailer": "^6.7.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cpx": "^1.5.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prisma": "^2.29.1"
|
||||
"prisma": "^3.7.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import NextAuth, { NextAuthOptions } from "next-auth"
|
||||
import EmailProvider from "next-auth/providers/email"
|
||||
// import EmailProvider from "next-auth/providers/email"
|
||||
import GitHubProvider from "next-auth/providers/github"
|
||||
import Auth0Provider from "next-auth/providers/auth0"
|
||||
import KeycloakProvider from "next-auth/providers/keycloak"
|
||||
import TwitterProvider from "next-auth/providers/twitter"
|
||||
import TwitterProvider, {
|
||||
TwitterLegacy as TwitterLegacyProvider,
|
||||
} from "next-auth/providers/twitter"
|
||||
import CredentialsProvider from "next-auth/providers/credentials"
|
||||
import IDS4Provider from "next-auth/providers/identity-server4"
|
||||
import Twitch from "next-auth/providers/twitch"
|
||||
@@ -25,6 +27,7 @@ import Okta from "next-auth/providers/okta"
|
||||
import AzureB2C from "next-auth/providers/azure-ad-b2c"
|
||||
import OsuProvider from "next-auth/providers/osu"
|
||||
import AppleProvider from "next-auth/providers/apple"
|
||||
import PatreonProvider from "next-auth/providers/patreon"
|
||||
|
||||
// import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
// import { PrismaClient } from "@prisma/client"
|
||||
@@ -71,11 +74,17 @@ export const authOptions: NextAuthOptions = {
|
||||
},
|
||||
}),
|
||||
// OAuth 1
|
||||
// TwitterLegacyProvider({
|
||||
// clientId: process.env.TWITTER_LEGACY_ID,
|
||||
// clientSecret: process.env.TWITTER_LEGACY_SECRET,
|
||||
// }),
|
||||
// OAuth 2 / OIDC
|
||||
TwitterProvider({
|
||||
// Opt-in to the new Twitter API for now. Should be default in the future.
|
||||
version: "2.0",
|
||||
clientId: process.env.TWITTER_ID,
|
||||
clientSecret: process.env.TWITTER_SECRET,
|
||||
}),
|
||||
// OAuth 2 / OIDC
|
||||
GitHubProvider({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET,
|
||||
@@ -177,6 +186,10 @@ export const authOptions: NextAuthOptions = {
|
||||
clientId: process.env.APPLE_ID,
|
||||
clientSecret: process.env.APPLE_SECRET,
|
||||
}),
|
||||
PatreonProvider({
|
||||
clientId: process.env.PATREON_ID,
|
||||
clientSecret: process.env.PATREON_SECRET,
|
||||
})
|
||||
],
|
||||
secret: process.env.SECRET,
|
||||
debug: true,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"incremental": true,
|
||||
"jsx": "preserve",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
|
||||
7986
package-lock.json
generated
7986
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
85
package.json
85
package.json
@@ -65,13 +65,13 @@
|
||||
],
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.15.4",
|
||||
"@panva/hkdf": "^1.0.0",
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@panva/hkdf": "^1.0.1",
|
||||
"cookie": "^0.4.1",
|
||||
"jose": "^4.1.2",
|
||||
"jose": "^4.3.7",
|
||||
"oauth": "^0.9.15",
|
||||
"openid-client": "^5.0.2",
|
||||
"preact": "^10.5.14",
|
||||
"openid-client": "^5.1.0",
|
||||
"preact": "^10.6.3",
|
||||
"preact-render-to-string": "^5.1.19",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
@@ -87,50 +87,47 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/core": "^1.6.0",
|
||||
"@babel/cli": "^7.15.7",
|
||||
"@babel/core": "^7.15.5",
|
||||
"@babel/plugin-proposal-optional-catch-binding": "^7.14.5",
|
||||
"@babel/plugin-transform-runtime": "^7.15.0",
|
||||
"@babel/preset-env": "^7.15.6",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@babel/cli": "^7.16.0",
|
||||
"@babel/core": "^7.16.0",
|
||||
"@babel/plugin-proposal-optional-catch-binding": "^7.16.0",
|
||||
"@babel/plugin-transform-runtime": "^7.16.4",
|
||||
"@babel/preset-env": "^7.16.4",
|
||||
"@babel/preset-react": "^7.16.0",
|
||||
"@babel/preset-typescript": "^7.16.0",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
"@testing-library/react-hooks": "^7.0.2",
|
||||
"@testing-library/user-event": "^13.2.1",
|
||||
"@types/node": "^16.11.6",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/node": "^16.11.12",
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
"@types/oauth": "^0.9.1",
|
||||
"@types/react": "^17.0.27",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||
"@types/react": "^17.0.37",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@typescript-eslint/parser": "^4.33.0",
|
||||
"autoprefixer": "^10.3.7",
|
||||
"babel-jest": "^27.3.0",
|
||||
"autoprefixer": "^10.4.0",
|
||||
"babel-jest": "^27.4.2",
|
||||
"babel-plugin-jsx-pragmatic": "^1.0.2",
|
||||
"babel-preset-preact": "^2.0.0",
|
||||
"conventional-changelog-conventionalcommits": "4.6.1",
|
||||
"cssnano": "^5.0.8",
|
||||
"cssnano": "^5.0.12",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"eslint-plugin-import": "^2.24.2",
|
||||
"eslint-plugin-jest": "^25.2.2",
|
||||
"eslint-plugin-jest": "^25.3.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"husky": "^7.0.2",
|
||||
"jest": "^27.3.0",
|
||||
"husky": "^7.0.4",
|
||||
"jest": "^27.4.3",
|
||||
"jest-watch-typeahead": "^1.0.0",
|
||||
"msw": "^0.35.0",
|
||||
"next": "v11.1.3-canary.0",
|
||||
"postcss-cli": "^9.0.1",
|
||||
"msw": "^0.36.3",
|
||||
"next": "12.0.7",
|
||||
"postcss-cli": "^9.0.2",
|
||||
"postcss-nested": "^5.0.6",
|
||||
"prettier": "^2.4.1",
|
||||
"pretty-quick": "^3.1.1",
|
||||
"prettier": "2.4.1",
|
||||
"pretty-quick": "^3.1.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"typescript": "^4.4.3",
|
||||
"typescript": "^4.5.2",
|
||||
"whatwg-fetch": "^3.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -142,9 +139,12 @@
|
||||
"eslintConfig": {
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json"
|
||||
"project": "./tsconfig.eslint.json"
|
||||
},
|
||||
"extends": ["standard-with-typescript", "prettier"],
|
||||
"extends": [
|
||||
"standard-with-typescript",
|
||||
"prettier"
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"node_modules",
|
||||
"next-env.d.ts",
|
||||
@@ -168,15 +168,26 @@
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["./**/*test.js"],
|
||||
"files": [
|
||||
"./**/*test.js"
|
||||
],
|
||||
"env": {
|
||||
"jest/globals": true
|
||||
},
|
||||
"extends": ["plugin:jest/recommended"],
|
||||
"plugins": ["jest"]
|
||||
"extends": [
|
||||
"plugin:jest/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"jest"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"eslintIgnore": [
|
||||
"./*.d.ts",
|
||||
"**/tests",
|
||||
"**/__tests__"
|
||||
],
|
||||
"release": {
|
||||
"branches": [
|
||||
"+([0-9])?(.{+([0-9]),x}).x",
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { ErrorType } from "./pages/error"
|
||||
|
||||
export interface IncomingRequest {
|
||||
/** @default "http://localhost:3000" */
|
||||
host: string
|
||||
host?: string
|
||||
method?: string
|
||||
cookies?: Record<string, string>
|
||||
headers?: Record<string, any>
|
||||
@@ -92,8 +92,11 @@ export async function NextAuthHandler<
|
||||
switch (action) {
|
||||
case "providers":
|
||||
return (await routes.providers(options.providers)) as any
|
||||
case "session":
|
||||
return (await routes.session({ options, sessionStore })) as any
|
||||
case "session": {
|
||||
const session = await routes.session({ options, sessionStore })
|
||||
if (session.cookies) cookies.push(...session.cookies)
|
||||
return { ...session, cookies } as any
|
||||
}
|
||||
case "csrf":
|
||||
return {
|
||||
headers: [{ key: "Content-Type", value: "application/json" }],
|
||||
@@ -135,15 +138,6 @@ export async function NextAuthHandler<
|
||||
}
|
||||
return render.verifyRequest()
|
||||
case "error":
|
||||
if (pages.error) {
|
||||
return {
|
||||
redirect: `${pages.error}${
|
||||
pages.error.includes("?") ? "&" : "?"
|
||||
}error=${error}`,
|
||||
cookies,
|
||||
}
|
||||
}
|
||||
|
||||
// These error messages are displayed in line on the sign in page
|
||||
if (
|
||||
[
|
||||
@@ -162,6 +156,15 @@ export async function NextAuthHandler<
|
||||
return { redirect: `${options.url}/signin?error=${error}`, cookies }
|
||||
}
|
||||
|
||||
if (pages.error) {
|
||||
return {
|
||||
redirect: `${pages.error}${
|
||||
pages.error.includes("?") ? "&" : "?"
|
||||
}error=${error}`,
|
||||
cookies,
|
||||
}
|
||||
}
|
||||
|
||||
return render.error({ error: error as ErrorType })
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ export async function init({
|
||||
// Callback functions
|
||||
callbacks: { ...defaultCallbacks, ...userOptions.callbacks },
|
||||
logger,
|
||||
callbackUrl: process.env.NEXTAUTH_URL ?? "http://localhost:3000",
|
||||
callbackUrl: url.origin,
|
||||
}
|
||||
|
||||
// Init cookies
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { CallbackParamsType, TokenSet } from "openid-client"
|
||||
import { TokenSet } from "openid-client"
|
||||
import { openidClient } from "./client"
|
||||
import { oAuth1Client } from "./client-legacy"
|
||||
import { useState } from "./state-handler"
|
||||
import { usePKCECodeVerifier } from "./pkce-handler"
|
||||
import { OAuthCallbackError } from "../../errors"
|
||||
|
||||
import type { CallbackParamsType } from "openid-client"
|
||||
import type { Account, LoggerInstance, Profile } from "../../.."
|
||||
import type { OAuthChecks, OAuthConfig } from "../../../providers"
|
||||
import type { InternalOptions } from "../../../lib/types"
|
||||
|
||||
@@ -12,7 +12,9 @@ export async function openidClient(
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<Client> {
|
||||
const provider = options.provider
|
||||
|
||||
|
||||
if (provider.httpOptions) custom.setHttpOptionsDefaults(provider.httpOptions)
|
||||
|
||||
let issuer: Issuer
|
||||
if (provider.wellKnown) {
|
||||
issuer = await Issuer.discover(provider.wellKnown)
|
||||
@@ -44,7 +46,5 @@ export async function openidClient(
|
||||
// and https://github.com/nextauthjs/next-auth/issues/3067
|
||||
client[custom.clock_tolerance] = 10
|
||||
|
||||
if (provider.httpOptions) custom.setHttpOptionsDefaults(provider.httpOptions)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { InternalProvider } from "../../lib/types"
|
||||
import { Provider } from "../../providers"
|
||||
import { merge } from "../../lib/merge"
|
||||
import { InternalUrl } from "../../lib/parse-url"
|
||||
|
||||
import type { InternalProvider } from "../../lib/types"
|
||||
import type { Provider } from "../../providers"
|
||||
import type { InternalUrl } from "../../lib/parse-url"
|
||||
|
||||
/**
|
||||
* Adds `signinUrl` and `callbackUrl` to each provider
|
||||
@@ -36,7 +37,7 @@ export default function parseProviders(params: {
|
||||
function normalizeProvider(provider?: Provider) {
|
||||
if (!provider) return
|
||||
|
||||
const normalizedProvider: InternalProvider = Object.entries(
|
||||
const normalized: InternalProvider = Object.entries(
|
||||
provider
|
||||
).reduce<InternalProvider>((acc, [key, value]) => {
|
||||
if (
|
||||
@@ -44,25 +45,29 @@ function normalizeProvider(provider?: Provider) {
|
||||
typeof value === "string"
|
||||
) {
|
||||
const url = new URL(value)
|
||||
;(acc as any)[key] = {
|
||||
acc[key] = {
|
||||
url: `${url.origin}${url.pathname}`,
|
||||
params: Object.fromEntries(url.searchParams ?? []),
|
||||
}
|
||||
} else {
|
||||
acc[key as keyof InternalProvider] = value
|
||||
acc[key] = value
|
||||
}
|
||||
|
||||
return acc
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter, @typescript-eslint/consistent-type-assertions
|
||||
}, {} as InternalProvider)
|
||||
}, {} as any)
|
||||
|
||||
// Checks only work on OAuth 2.x + OIDC providers
|
||||
if (
|
||||
provider.type === "oauth" &&
|
||||
!provider.version?.startsWith("1.") &&
|
||||
!provider.checks
|
||||
) {
|
||||
;(normalizedProvider as InternalProvider<"oauth">).checks = ["state"]
|
||||
if (normalized.type === "oauth" && !normalized.version?.startsWith("1.")) {
|
||||
// If provider has as an "openid-configuration" well-known endpoint
|
||||
// or an "openid" scope request, it will also likely be able to receive an `id_token`
|
||||
normalized.idToken = Boolean(
|
||||
normalized.idToken ??
|
||||
normalized.wellKnown?.includes("openid-configuration") ??
|
||||
// @ts-expect-error
|
||||
normalized.authorization?.params?.scope?.includes("openid")
|
||||
)
|
||||
|
||||
if (!normalized.checks) normalized.checks = ["state"]
|
||||
}
|
||||
return normalizedProvider
|
||||
return normalized
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function SigninPage(props: SignInServerPageParams) {
|
||||
Callback: "Try signing in with a different account.",
|
||||
OAuthAccountNotLinked:
|
||||
"To confirm your identity, sign in with the same account you used originally.",
|
||||
EmailSignin: "Check your email inbox.",
|
||||
EmailSignin: "The e-mail could not be sent.",
|
||||
CredentialsSignin:
|
||||
"Sign in failed. Check the details you provided are correct.",
|
||||
SessionRequired: "Please sign in to access this page.",
|
||||
@@ -130,8 +130,7 @@ export default function SigninPage(props: SignInServerPageParams) {
|
||||
id={`input-${credential}-for-${provider.id}-provider`}
|
||||
type={provider.credentials[credential].type ?? "text"}
|
||||
placeholder={
|
||||
provider.credentials[credential].placeholder ??
|
||||
"Password"
|
||||
provider.credentials[credential].placeholder ?? ""
|
||||
}
|
||||
{...provider.credentials[credential]}
|
||||
/>
|
||||
|
||||
@@ -298,7 +298,7 @@ export interface CallbacksOptions<
|
||||
* This callback is called whenever a session is checked.
|
||||
* (Eg.: invoking the `/api/session` endpoint, using `useSession` or `getSession`)
|
||||
*
|
||||
* ⚠ By default, only a subset (email, name, imgage)
|
||||
* ⚠ By default, only a subset (email, name, image)
|
||||
* of the token is returned for increased security.
|
||||
*
|
||||
* If you want to make something available you added to the token through the `jwt` callback,
|
||||
|
||||
@@ -3,7 +3,7 @@ import hkdf from "@panva/hkdf"
|
||||
import { v4 as uuid } from "uuid"
|
||||
import { SessionStore } from "../core/lib/cookie"
|
||||
import type { NextApiRequest } from "next"
|
||||
import type { JWT, JWTDecodeParams, JWTEncodeParams } from "./types"
|
||||
import type { JWT, JWTDecodeParams, JWTEncodeParams, JWTOptions } from "./types"
|
||||
import type { LoggerInstance } from ".."
|
||||
|
||||
export * from "./types"
|
||||
@@ -56,7 +56,7 @@ export interface GetTokenParams<R extends boolean = false> {
|
||||
*/
|
||||
raw?: R
|
||||
secret: string
|
||||
decode?: typeof decode
|
||||
decode?: JWTOptions["decode"]
|
||||
logger?: LoggerInstance | Console
|
||||
}
|
||||
|
||||
@@ -70,10 +70,8 @@ export async function getToken<R extends boolean = false>(
|
||||
): Promise<R extends true ? string : JWT | null> {
|
||||
const {
|
||||
req,
|
||||
secureCookie = !(
|
||||
!process.env.NEXTAUTH_URL ||
|
||||
process.env.NEXTAUTH_URL.startsWith("http://")
|
||||
),
|
||||
secureCookie = process.env.NEXTAUTH_URL?.startsWith("https://") ??
|
||||
!!process.env.VERCEL,
|
||||
cookieName = secureCookie
|
||||
? "__Secure-next-auth.session-token"
|
||||
: "next-auth.session-token",
|
||||
@@ -90,7 +88,13 @@ export async function getToken<R extends boolean = false>(
|
||||
logger
|
||||
)
|
||||
|
||||
const token = sessionStore.value
|
||||
let token = sessionStore.value
|
||||
|
||||
if (!token && req.headers.authorization?.split(" ")[0] === "Bearer") {
|
||||
const urlEncodedToken = req.headers.authorization.split(" ")[1]
|
||||
token = decodeURIComponent(urlEncodedToken)
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
if (!token) return null
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { decode, encode } from "."
|
||||
import type { Awaitable } from ".."
|
||||
|
||||
export interface DefaultJWT extends Record<string, unknown> {
|
||||
name?: string | null
|
||||
@@ -42,9 +42,9 @@ export interface JWTOptions {
|
||||
*/
|
||||
maxAge: number
|
||||
/** Override this method to control the NextAuth.js issued JWT encoding. */
|
||||
encode: typeof encode
|
||||
encode: (params: JWTEncodeParams) => Awaitable<string>
|
||||
/** Override this method to control the NextAuth.js issued JWT decoding. */
|
||||
decode: typeof decode
|
||||
decode: (params: JWTDecodeParams) => Awaitable<JWT | null>
|
||||
}
|
||||
|
||||
export type Secret = string | Buffer
|
||||
|
||||
@@ -50,7 +50,7 @@ export type NextAuthAction =
|
||||
export interface InternalOptions<T extends ProviderType = any> {
|
||||
providers: InternalProvider[]
|
||||
/**
|
||||
* Parsed from `NEXTAUTH_URL` or `VERCEL_URL`.
|
||||
* Parsed from `NEXTAUTH_URL` or `x-forwarded-host` on Vercel.
|
||||
* @default "http://localhost:3000/api/auth"
|
||||
*/
|
||||
url: InternalUrl
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextAuthHandler } from "../core"
|
||||
import { setCookie } from "./cookie"
|
||||
import { setCookie, detectHost } from "./utils"
|
||||
|
||||
import type {
|
||||
GetServerSidePropsContext,
|
||||
@@ -21,7 +21,7 @@ async function NextAuthNextHandler(
|
||||
const { nextauth, ...query } = req.query
|
||||
const handler = await NextAuthHandler({
|
||||
req: {
|
||||
host: (process.env.NEXTAUTH_URL ?? process.env.VERCEL_URL) as string,
|
||||
host: detectHost(req.headers["x-forwarded-host"]),
|
||||
body: req.body,
|
||||
query,
|
||||
cookies: req.cookies,
|
||||
@@ -87,7 +87,7 @@ export async function getServerSession(
|
||||
const session = await NextAuthHandler<Session | {}>({
|
||||
options,
|
||||
req: {
|
||||
host: (process.env.NEXTAUTH_URL ?? process.env.VERCEL_URL) as string,
|
||||
host: detectHost(context.req.headers["x-forwarded-host"]),
|
||||
action: "session",
|
||||
method: "GET",
|
||||
cookies: context.req.cookies,
|
||||
@@ -108,7 +108,7 @@ declare global {
|
||||
namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
NEXTAUTH_URL?: string
|
||||
VERCEL_URL?: string
|
||||
VERCEL?: "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,11 @@ export function setCookie(res, cookie: Cookie) {
|
||||
setCookieHeader.push(cookieHeader)
|
||||
res.setHeader("Set-Cookie", setCookieHeader)
|
||||
}
|
||||
|
||||
/** Extract the host from the environment */
|
||||
export function detectHost(forwardedHost: any) {
|
||||
// If we detect a Vercel environment, we can trust the host
|
||||
if (process.env.VERCEL) return forwardedHost
|
||||
// If `NEXTAUTH_URL` is `undefined` we fall back to "http://localhost:3000"
|
||||
return process.env.NEXTAUTH_URL
|
||||
}
|
||||
@@ -168,7 +168,7 @@ export default function FortyTwo<
|
||||
userinfo: "https://api.intra.42.fr/v2/me",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
id: profile.id.toString(),
|
||||
name: profile.usual_full_name,
|
||||
email: profile.email,
|
||||
image: profile.image_url,
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface Auth0Profile {
|
||||
sub: string
|
||||
nicname: string
|
||||
nickname: string
|
||||
email: string
|
||||
picture: string
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function AzureAD<P extends Record<string, any> = AzureADProfile>(
|
||||
wellKnown: `https://login.microsoftonline.com/${tenant}/v2.0/.well-known/openid-configuration`,
|
||||
authorization: {
|
||||
params: {
|
||||
scope: "User.Read",
|
||||
scope: "openid profile email",
|
||||
},
|
||||
},
|
||||
async profile(profile, tokens) {
|
||||
|
||||
@@ -15,6 +15,7 @@ export default function Cognito<P extends Record<string, any> = CognitoProfile>(
|
||||
name: "Cognito",
|
||||
type: "oauth",
|
||||
wellKnown: `${options.issuer}/.well-known/openid-configuration`,
|
||||
idToken: true,
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/** @type {import(".").OAuthProvider} */
|
||||
export default function FusionAuth(options) {
|
||||
return {
|
||||
id: "fusionauth",
|
||||
name: "FusionAuth",
|
||||
type: "oauth",
|
||||
authorization: `${options.issuer}oauth2/authorize`,
|
||||
token: `${options.issuer}oauth2/token`,
|
||||
userinfo: `${options.issuer}oauth2/userinfo`,
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.picture,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
52
src/providers/fusionauth.ts
Normal file
52
src/providers/fusionauth.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "./oauth"
|
||||
|
||||
/** This is the default openid signature returned from FusionAuth
|
||||
* it can be customized using [lambda functions](https://fusionauth.io/docs/v1/tech/lambdas)
|
||||
*/
|
||||
export interface FusionAuthProfile {
|
||||
aud: string
|
||||
exp: number
|
||||
iat: number
|
||||
iss: string
|
||||
sub: string
|
||||
jti: string
|
||||
authenticationType: string
|
||||
email: string
|
||||
email_verified: boolean
|
||||
preferred_username: string
|
||||
at_hash: string
|
||||
c_hash: string
|
||||
scope: string
|
||||
sid: string
|
||||
}
|
||||
|
||||
export default function FusionAuth<
|
||||
P extends Record<string, any> = FusionAuthProfile
|
||||
>(
|
||||
// tenantId only needed if there is more than one tenant configured on the server
|
||||
options: OAuthUserConfig<P> & { tenantId?: string }
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "fusionauth",
|
||||
name: "FusionAuth",
|
||||
type: "oauth",
|
||||
wellKnown: options?.tenantId
|
||||
? `${options.issuer}/.well-known/openid-configuration?tenantId=${options.tenantId}`
|
||||
: `${options.issuer}/.well-known/openid-configuration`,
|
||||
authorization: {
|
||||
params: {
|
||||
scope: "openid offline_access",
|
||||
...(options?.tenantId && { tenantId: options.tenantId }),
|
||||
},
|
||||
},
|
||||
checks: ["pkce", "state"],
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
email: profile.email,
|
||||
name: profile?.preferred_username,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,21 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface GoogleProfile {
|
||||
sub: string
|
||||
name: string
|
||||
aud: string
|
||||
azp: string
|
||||
email: string
|
||||
email_verified: boolean
|
||||
exp: number
|
||||
family_name: string
|
||||
given_name: string
|
||||
hd: string
|
||||
iat: number
|
||||
iss: string
|
||||
jti: string
|
||||
name: string
|
||||
nbf: number
|
||||
picture: string
|
||||
sub: string
|
||||
}
|
||||
|
||||
export default function Google<P extends Record<string, any> = GoogleProfile>(
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/** @type {import(".").OAuthProvider} */
|
||||
export default function Kakao(options) {
|
||||
return {
|
||||
id: "kakao",
|
||||
name: "Kakao",
|
||||
type: "oauth",
|
||||
authorization: "https://kauth.kakao.com/oauth/authorize",
|
||||
token: "https://kauth.kakao.com/oauth/token",
|
||||
userinfo: "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,
|
||||
}
|
||||
}
|
||||
92
src/providers/kakao.ts
Normal file
92
src/providers/kakao.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export type DateTime = string
|
||||
export type Gender = "female" | "male"
|
||||
export type AgeRange =
|
||||
| "1-9"
|
||||
| "10-14"
|
||||
| "15-19"
|
||||
| "20-29"
|
||||
| "30-39"
|
||||
| "40-49"
|
||||
| "50-59"
|
||||
| "60-69"
|
||||
| "70-79"
|
||||
| "80-89"
|
||||
| "90-"
|
||||
|
||||
/**
|
||||
* https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#req-user-info
|
||||
* type from : https://gist.github.com/ziponia/cdce1ebd88f979b2a6f3f53416b56a77
|
||||
*/
|
||||
export interface KakaoProfile {
|
||||
id: number
|
||||
has_signed_up?: boolean
|
||||
connected_at?: DateTime
|
||||
synched_at?: DateTime
|
||||
properties?: {
|
||||
id?: string
|
||||
status?: string
|
||||
registered_at?: DateTime
|
||||
msg_blocked?: boolean
|
||||
nickname?: string
|
||||
profile_image?: string
|
||||
thumbnail_image?: string
|
||||
}
|
||||
kakao_account?: {
|
||||
profile_needs_agreement?: boolean
|
||||
profile_nickname_needs_agreement?: boolean
|
||||
profile_image_needs_agreement?: boolean
|
||||
profile?: {
|
||||
nickname?: string
|
||||
thumbnail_image_url?: string
|
||||
profile_image_url?: string
|
||||
is_default_image?: boolean
|
||||
}
|
||||
name_needs_agreement?: boolean
|
||||
name?: string
|
||||
email_needs_agreement?: boolean
|
||||
is_email_valid?: boolean
|
||||
is_email_verified?: boolean
|
||||
email?: string
|
||||
age_range_needs_agreement?: boolean
|
||||
age_range?: AgeRange
|
||||
birthyear_needs_agreement?: boolean
|
||||
birthyear?: string
|
||||
birthday_needs_agreement?: boolean
|
||||
birthday?: string
|
||||
birthday_type?: string
|
||||
gender_needs_agreement?: boolean
|
||||
gender?: Gender
|
||||
phone_number_needs_agreement?: boolean
|
||||
phone_number?: string
|
||||
ci_needs_agreement?: boolean
|
||||
ci?: string
|
||||
ci_authenticated_at?: DateTime
|
||||
}
|
||||
}
|
||||
|
||||
export default function Kakao<P extends Record<string, any> = KakaoProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "kakao",
|
||||
name: "Kakao",
|
||||
type: "oauth",
|
||||
authorization: "https://kauth.kakao.com/oauth/authorize?scope",
|
||||
token: "https://kauth.kakao.com/oauth/token",
|
||||
userinfo: "https://kapi.kakao.com/v2/user/me",
|
||||
client: {
|
||||
token_endpoint_auth_method: "client_secret_post",
|
||||
},
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/** @type {import(".").OAuthProvider} */
|
||||
export default function Naver(options) {
|
||||
return {
|
||||
id: "naver",
|
||||
name: "Naver",
|
||||
type: "oauth",
|
||||
authorization: "https://nid.naver.com/oauth2.0/authorize",
|
||||
token: "https://nid.naver.com/oauth2.0/token",
|
||||
userinfo: "https://openapi.naver.com/v1/nid/me",
|
||||
profile(profile) {
|
||||
// REVIEW: By default, we only want to expose the
|
||||
// "id", "name", "email" and "image" fields.
|
||||
return profile.response
|
||||
},
|
||||
checks: ["state"],
|
||||
options,
|
||||
}
|
||||
}
|
||||
42
src/providers/naver.ts
Normal file
42
src/providers/naver.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
/** https://developers.naver.com/docs/login/profile/profile.md */
|
||||
export interface NaverProfile {
|
||||
resultcode: string
|
||||
message: string
|
||||
response: {
|
||||
id: string
|
||||
nickname?: string
|
||||
name?: string
|
||||
email?: string
|
||||
gender?: "F" | "M" | "U"
|
||||
age?: string
|
||||
birthday?: string
|
||||
profile_image?: string
|
||||
birthyear?: string
|
||||
mobile?: string
|
||||
}
|
||||
}
|
||||
|
||||
export default function Naver<P extends Record<string, any> = NaverProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "naver",
|
||||
name: "Naver",
|
||||
type: "oauth",
|
||||
authorization: "https://nid.naver.com/oauth2.0/authorize",
|
||||
token: "https://nid.naver.com/oauth2.0/token",
|
||||
userinfo: "https://openapi.naver.com/v1/nid/me",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.response.id,
|
||||
name: profile.response.name,
|
||||
email: profile.response.email,
|
||||
image: profile.response.profile_image,
|
||||
}
|
||||
},
|
||||
checks: ["state"],
|
||||
options,
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ export default function Okta<P extends Record<string, any> = OktaProfile>(
|
||||
type: "oauth",
|
||||
wellKnown: `${options.issuer}/.well-known/openid-configuration`,
|
||||
authorization: { params: { scope: "openid email profile" } },
|
||||
idToken: true,
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
|
||||
34
src/providers/patreon.ts
Normal file
34
src/providers/patreon.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface PatreonProfile {
|
||||
sub: string
|
||||
nickname: string
|
||||
email: string
|
||||
picture: string
|
||||
}
|
||||
|
||||
export default function Patreon<P extends Record<string, any> = PatreonProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "patreon",
|
||||
name: "Patreon",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
authorization: {
|
||||
url: "https://www.patreon.com/oauth2/authorize",
|
||||
params: { scope: "identity identity[email]" },
|
||||
},
|
||||
token: "https://www.patreon.com/api/oauth2/token",
|
||||
userinfo: "https://www.patreon.com/api/oauth2/api/current_user",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.data.id,
|
||||
name: profile.data.attributes.full_name,
|
||||
email: profile.data.attributes.email,
|
||||
image: profile.data.attributes.image_url,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,23 @@ export default function Strava(options) {
|
||||
id: "strava",
|
||||
name: "Strava",
|
||||
type: "oauth",
|
||||
authorization: "https://www.strava.com/api/v3/oauth/authorize?scope=read",
|
||||
token: "https://www.strava.com/api/v3/oauth/token",
|
||||
authorization: {
|
||||
url: "https://www.strava.com/api/v3/oauth/authorize",
|
||||
params: {
|
||||
scope: "read",
|
||||
approval_prompt: "auto",
|
||||
response_type: "code",
|
||||
redirect_uri: "http://localhost:3000/api/auth/callback/strava",
|
||||
},
|
||||
},
|
||||
token: {
|
||||
url: "https://www.strava.com/api/v3/oauth/token",
|
||||
},
|
||||
userinfo: "https://www.strava.com/api/v3/athlete",
|
||||
client: {
|
||||
token_endpoint_auth_method: "client_secret_post",
|
||||
},
|
||||
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface TwitterProfile {
|
||||
export interface TwitterLegacyProfile {
|
||||
id: number
|
||||
id_str: string
|
||||
name: string
|
||||
@@ -95,12 +95,12 @@ export interface TwitterProfile {
|
||||
needs_phone_verification: boolean
|
||||
}
|
||||
|
||||
export default function Twitter<P extends Record<string, any> = TwitterProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
export function TwitterLegacy<
|
||||
P extends Record<string, any> = TwitterLegacyProfile
|
||||
>(options: OAuthUserConfig<P>): OAuthConfig<P> {
|
||||
return {
|
||||
id: "twitter",
|
||||
name: "Twitter",
|
||||
name: "Twitter (Legacy)",
|
||||
type: "oauth",
|
||||
version: "1.0A",
|
||||
authorization: "https://api.twitter.com/oauth/authenticate",
|
||||
@@ -122,3 +122,98 @@ export default function Twitter<P extends Record<string, any> = TwitterProfile>(
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [Documentation](https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me)
|
||||
*/
|
||||
export interface TwitterProfile {
|
||||
data: {
|
||||
id: string
|
||||
name: string
|
||||
username: string
|
||||
location?: string
|
||||
entities?: {
|
||||
url: {
|
||||
urls: Array<{
|
||||
start: number
|
||||
end: number
|
||||
url: string
|
||||
expanded_url: string
|
||||
display_url: string
|
||||
}>
|
||||
}
|
||||
description: {
|
||||
hashtags: Array<{
|
||||
start: number
|
||||
end: number
|
||||
tag: string
|
||||
}>
|
||||
}
|
||||
}
|
||||
verified?: boolean
|
||||
description?: string
|
||||
url?: string
|
||||
profile_image_url?: string
|
||||
protected?: boolean
|
||||
pinned_tweet_id?: string
|
||||
created_at?: string
|
||||
}
|
||||
includes?: {
|
||||
tweets?: Array<{
|
||||
id: string
|
||||
text: string
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
let warned = false
|
||||
export default function Twitter<
|
||||
P extends Record<string, any> = TwitterLegacyProfile | TwitterProfile
|
||||
>(options: OAuthUserConfig<P>): OAuthConfig<P> {
|
||||
if (!warned && options.version === "2.0") {
|
||||
warned = true
|
||||
console.warn(
|
||||
"Opted-in to Twitter OAuth 2.0. See the docs https://next-auth.js.org/providers/twitter#oauth-2"
|
||||
)
|
||||
return {
|
||||
id: "twitter",
|
||||
name: "Twitter",
|
||||
version: "2.0",
|
||||
type: "oauth",
|
||||
authorization: {
|
||||
url: "https://twitter.com/i/oauth2/authorize",
|
||||
params: { scope: "users.read tweet.read offline.access" },
|
||||
},
|
||||
token: {
|
||||
url: "https://api.twitter.com/2/oauth2/token",
|
||||
// TODO: Remove this
|
||||
async request({ client, params, checks, provider }) {
|
||||
const response = await client.oauthCallback(
|
||||
provider.callbackUrl,
|
||||
params,
|
||||
checks,
|
||||
{ exchangeBody: { client_id: options.clientId } }
|
||||
)
|
||||
return { tokens: response }
|
||||
},
|
||||
},
|
||||
userinfo: {
|
||||
url: "https://api.twitter.com/2/users/me",
|
||||
params: { "user.fields": "profile_image_url" },
|
||||
},
|
||||
profile({ data }) {
|
||||
return {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
// NOTE: E-mail is currently unsupported by OAuth 2 Twitter.
|
||||
email: null,
|
||||
image: data.profile_image_url,
|
||||
}
|
||||
},
|
||||
checks: ["pkce", "state"],
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
return TwitterLegacy(options)
|
||||
}
|
||||
|
||||
4
tsconfig.eslint.json
Normal file
4
tsconfig.eslint.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["./*.d.ts", "**/tests", "**/__tests__"]
|
||||
}
|
||||
Reference in New Issue
Block a user