mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
33 Commits
@next-auth
...
next-auth@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6590993fdc | ||
|
|
0ea96796b2 | ||
|
|
8ec940bd6a | ||
|
|
e3bcdf83f1 | ||
|
|
4084297334 | ||
|
|
c9827960b1 | ||
|
|
946a825865 | ||
|
|
c57d8c997e | ||
|
|
e2b92bf04f | ||
|
|
8bff050e4e | ||
|
|
1a79a1a612 | ||
|
|
b7065a602f | ||
|
|
61b92ec1b6 | ||
|
|
282f7ab340 | ||
|
|
4f56e414b0 | ||
|
|
2725d07eb7 | ||
|
|
5a8b029523 | ||
|
|
f62a985848 | ||
|
|
edd6fb5989 | ||
|
|
fb60554a62 | ||
|
|
9784dfb631 | ||
|
|
4ff836a8cf | ||
|
|
042955eaaa | ||
|
|
82e107c0e7 | ||
|
|
f7050347e8 | ||
|
|
c56abbd745 | ||
|
|
3f6d99e8df | ||
|
|
46eedee3c8 | ||
|
|
bb664a27da | ||
|
|
a14fbea0b5 | ||
|
|
4705632c6b | ||
|
|
2296471f02 | ||
|
|
8853000fd5 |
15
.github/CODEOWNERS
vendored
15
.github/CODEOWNERS
vendored
@@ -1,4 +1,11 @@
|
||||
/types/ @balazsorban44 @lluia
|
||||
/docs/ @balazsorban44 @ndom91
|
||||
/adapters/ @balazsorban44 @ndom91
|
||||
/__tests__/ @lluia
|
||||
# Learn how to add code owners here:
|
||||
# https://help.github.com/en/articles/about-code-owners
|
||||
|
||||
* @balazsorban44
|
||||
.github @ThangHuuVu
|
||||
/apps/ @lluia @ndom91 @ThangHuuVu
|
||||
/docs/ @lluia @ndom91
|
||||
/packages/ @ThangHuuVu
|
||||
/packages/adapter-*/ @ndom91
|
||||
/**/*test* @lluia
|
||||
/**/*type* @lluia
|
||||
2
.github/pr-labeler.yml
vendored
2
.github/pr-labeler.yml
vendored
@@ -10,7 +10,7 @@ providers:
|
||||
|
||||
adapters:
|
||||
- packages/next-auth/src/adapters.ts
|
||||
- packages/*-adapter/**
|
||||
- packages/adapter-*/**
|
||||
|
||||
dgraph:
|
||||
- packages/adapter-dgraph/**
|
||||
|
||||
2
.github/version-pr/action.yml
vendored
2
.github/version-pr/action.yml
vendored
@@ -4,5 +4,5 @@ outputs:
|
||||
version:
|
||||
description: "npm package version"
|
||||
runs:
|
||||
using: "node12"
|
||||
using: "node16"
|
||||
main: "index.js"
|
||||
|
||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -108,7 +108,11 @@ jobs:
|
||||
- name: Comment version on PR
|
||||
uses: NejcZdovc/comment-pr@v1
|
||||
with:
|
||||
message: "🎉 Experimental release [published on npm](https://www.npmjs.com/package/next-auth/v/${{ env.VERSION }})!\n\n```sh\nnpm i next-auth@${{ env.VERSION }}\n```\n```sh\nyarn add next-auth@${{ env.VERSION }}\n```"
|
||||
message:
|
||||
"🎉 Experimental release [published 📦️ on npm](https://npmjs.com/package/next-auth/v/${{ env.VERSION }})!\n \
|
||||
```sh\npnpm add next-auth@${{ env.VERSION }}\n```\n \
|
||||
```sh\nyarn add next-auth@${{ env.VERSION }}\n```\n \
|
||||
```sh\nnpm i next-auth@${{ env.VERSION }}\n```"
|
||||
env:
|
||||
VERSION: ${{ steps.determine-version.outputs.version }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -47,9 +47,5 @@ EMAIL_FROM=user@gmail.com
|
||||
# MongoDB: DATABASE_URL=mongodb://nextauth:password@127.0.0.1:27017/nextauth?synchronize=true
|
||||
DATABASE_URL=
|
||||
|
||||
BOXYHQSAML_ISSUER="https://jackson-demo.boxyhq.com"
|
||||
BOXYHQSAML_ID="tenant=boxyhq.com&product=saml-demo.boxyhq.com"
|
||||
BOXYHQSAML_SECRET="dummy"
|
||||
|
||||
WIKIMEDIA_ID=
|
||||
WIKIMEDIA_SECRET=
|
||||
@@ -30,12 +30,11 @@ export const config = { matcher: ["/middleware-protected"] }
|
||||
// export default withAuth(
|
||||
// function middleware(req, ev) {
|
||||
// console.log(req, ev)
|
||||
// return undefined // NOTE: `NextMiddleware` should allow returning `void`
|
||||
// },
|
||||
// {
|
||||
// callbacks: {
|
||||
// authorized: ({ token }) => token.name === "Balázs Orbán",
|
||||
// }
|
||||
// },
|
||||
// }
|
||||
// )
|
||||
|
||||
|
||||
@@ -1,251 +1,134 @@
|
||||
import NextAuth, { NextAuthOptions } from "next-auth"
|
||||
// import EmailProvider from "next-auth/providers/email"
|
||||
import GitHubProvider from "next-auth/providers/github"
|
||||
import Auth0Provider from "next-auth/providers/auth0"
|
||||
import KeycloakProvider from "next-auth/providers/keycloak"
|
||||
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 DuendeIDS6Provider from "next-auth/providers/duende-identity-server6"
|
||||
import Twitch from "next-auth/providers/twitch"
|
||||
import GoogleProvider from "next-auth/providers/google"
|
||||
import FacebookProvider from "next-auth/providers/facebook"
|
||||
import FoursquareProvider from "next-auth/providers/foursquare"
|
||||
// import FreshbooksProvider from "next-auth/providers/freshbooks"
|
||||
import GitlabProvider from "next-auth/providers/gitlab"
|
||||
import InstagramProvider from "next-auth/providers/instagram"
|
||||
import LineProvider from "next-auth/providers/line"
|
||||
import LinkedInProvider from "next-auth/providers/linkedin"
|
||||
import MailchimpProvider from "next-auth/providers/mailchimp"
|
||||
import DiscordProvider from "next-auth/providers/discord"
|
||||
import AzureADProvider from "next-auth/providers/azure-ad"
|
||||
import SpotifyProvider from "next-auth/providers/spotify"
|
||||
import CognitoProvider from "next-auth/providers/cognito"
|
||||
import SlackProvider from "next-auth/providers/slack"
|
||||
import Okta from "next-auth/providers/okta"
|
||||
import NextAuth from "next-auth"
|
||||
import type { NextAuthOptions } from "next-auth"
|
||||
|
||||
// Providers
|
||||
import Apple from "next-auth/providers/apple"
|
||||
import Auth0 from "next-auth/providers/auth0"
|
||||
import AzureAD from "next-auth/providers/azure-ad"
|
||||
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 TraktProvider from "next-auth/providers/trakt"
|
||||
import WorkOSProvider from "next-auth/providers/workos"
|
||||
import BoxyHQSAMLProvider from "next-auth/providers/boxyhq-saml"
|
||||
import WikimediaProvider from "next-auth/providers/wikimedia"
|
||||
import VkProvider from "next-auth/providers/vk"
|
||||
import BoxyHQSAML from "next-auth/providers/boxyhq-saml"
|
||||
import Cognito from "next-auth/providers/cognito"
|
||||
import Credentials from "next-auth/providers/credentials"
|
||||
import Discord from "next-auth/providers/discord"
|
||||
import DuendeIDS6 from "next-auth/providers/duende-identity-server6"
|
||||
import Email from "next-auth/providers/email"
|
||||
import Facebook from "next-auth/providers/facebook"
|
||||
import Foursquare from "next-auth/providers/foursquare"
|
||||
import Freshbooks from "next-auth/providers/freshbooks"
|
||||
import GitHub from "next-auth/providers/github"
|
||||
import Gitlab from "next-auth/providers/gitlab"
|
||||
import Google from "next-auth/providers/google"
|
||||
import IDS4 from "next-auth/providers/identity-server4"
|
||||
import Instagram from "next-auth/providers/instagram"
|
||||
import Keycloak from "next-auth/providers/keycloak"
|
||||
import Line from "next-auth/providers/line"
|
||||
import LinkedIn from "next-auth/providers/linkedin"
|
||||
import Mailchimp from "next-auth/providers/mailchimp"
|
||||
import Okta from "next-auth/providers/okta"
|
||||
import Osu from "next-auth/providers/osu"
|
||||
import Patreon from "next-auth/providers/patreon"
|
||||
import Slack from "next-auth/providers/slack"
|
||||
import Spotify from "next-auth/providers/spotify"
|
||||
import Trakt from "next-auth/providers/trakt"
|
||||
import Twitch from "next-auth/providers/twitch"
|
||||
import Twitter, { TwitterLegacy } from "next-auth/providers/twitter"
|
||||
import Vk from "next-auth/providers/vk"
|
||||
import Wikimedia from "next-auth/providers/wikimedia"
|
||||
import WorkOS from "next-auth/providers/workos"
|
||||
|
||||
// TypeORM
|
||||
// import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
|
||||
// const adapter = TypeORMLegacyAdapter({
|
||||
// type: "sqlite",
|
||||
// name: "next-auth-test-memory",
|
||||
// database: "./typeorm/dev.db",
|
||||
// synchronize: true,
|
||||
// })
|
||||
// Adapters
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
import { Client as FaunaClient } from "faunadb"
|
||||
import { FaunaAdapter } from "@next-auth/fauna-adapter"
|
||||
import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
|
||||
|
||||
// // Prisma
|
||||
// import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
// import { PrismaClient } from "@prisma/client"
|
||||
// const prisma = new PrismaClient()
|
||||
// const adapter = PrismaAdapter(prisma)
|
||||
|
||||
// // Fauna
|
||||
// import { Client as FaunaClient } from "faunadb"
|
||||
// import { FaunaAdapter } from "@next-auth/fauna-adapter"
|
||||
// const client = new FaunaClient({
|
||||
// secret: process.env.FAUNA_SECRET,
|
||||
// domain: process.env.FAUNA_DOMAIN,
|
||||
// })
|
||||
// const adapter = FaunaAdapter(client)
|
||||
|
||||
// // Dummy
|
||||
// const adapter: any = {
|
||||
// getUserByEmail: (email) => ({ id: "1", email, emailVerified: null }),
|
||||
// createVerificationToken: (token) => token,
|
||||
// }
|
||||
|
||||
export const authOptions: NextAuthOptions = {
|
||||
// adapter,
|
||||
providers: [
|
||||
// E-mail
|
||||
// Start fake e-mail server with `npm run start:email`
|
||||
// EmailProvider({
|
||||
// server: {
|
||||
// host: "127.0.0.1",
|
||||
// auth: null,
|
||||
// secure: false,
|
||||
// port: 1025,
|
||||
// tls: { rejectUnauthorized: false },
|
||||
// },
|
||||
// }),
|
||||
// Credentials
|
||||
CredentialsProvider({
|
||||
name: "Credentials",
|
||||
credentials: {
|
||||
password: { label: "Password", type: "password" },
|
||||
},
|
||||
async authorize(credentials) {
|
||||
if (credentials.password === "pw") {
|
||||
return {
|
||||
name: "Fill Murray",
|
||||
email: "bill@fillmurray.com",
|
||||
image: "https://www.fillmurray.com/64/64",
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
}),
|
||||
// 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,
|
||||
}),
|
||||
GitHubProvider({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET,
|
||||
}),
|
||||
Auth0Provider({
|
||||
clientId: process.env.AUTH0_ID,
|
||||
clientSecret: process.env.AUTH0_SECRET,
|
||||
issuer: process.env.AUTH0_ISSUER,
|
||||
}),
|
||||
KeycloakProvider({
|
||||
clientId: process.env.KEYCLOAK_ID,
|
||||
clientSecret: process.env.KEYCLOAK_SECRET,
|
||||
issuer: process.env.KEYCLOAK_ISSUER,
|
||||
}),
|
||||
Twitch({
|
||||
clientId: process.env.TWITCH_ID,
|
||||
clientSecret: process.env.TWITCH_SECRET,
|
||||
}),
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_ID,
|
||||
clientSecret: process.env.GOOGLE_SECRET,
|
||||
}),
|
||||
FacebookProvider({
|
||||
clientId: process.env.FACEBOOK_ID,
|
||||
clientSecret: process.env.FACEBOOK_SECRET,
|
||||
}),
|
||||
FoursquareProvider({
|
||||
clientId: process.env.FOURSQUARE_ID,
|
||||
clientSecret: process.env.FOURSQUARE_SECRET,
|
||||
}),
|
||||
// FreshbooksProvider({
|
||||
// clientId: process.env.FRESHBOOKS_ID,
|
||||
// clientSecret: process.env.FRESHBOOKS_SECRET,
|
||||
// }),
|
||||
GitlabProvider({
|
||||
clientId: process.env.GITLAB_ID,
|
||||
clientSecret: process.env.GITLAB_SECRET,
|
||||
}),
|
||||
InstagramProvider({
|
||||
clientId: process.env.INSTAGRAM_ID,
|
||||
clientSecret: process.env.INSTAGRAM_SECRET,
|
||||
}),
|
||||
LineProvider({
|
||||
clientId: process.env.LINE_ID,
|
||||
clientSecret: process.env.LINE_SECRET,
|
||||
}),
|
||||
LinkedInProvider({
|
||||
clientId: process.env.LINKEDIN_ID,
|
||||
clientSecret: process.env.LINKEDIN_SECRET,
|
||||
}),
|
||||
MailchimpProvider({
|
||||
clientId: process.env.MAILCHIMP_ID,
|
||||
clientSecret: process.env.MAILCHIMP_SECRET,
|
||||
}),
|
||||
IDS4Provider({
|
||||
clientId: process.env.IDS4_ID,
|
||||
clientSecret: process.env.IDS4_SECRET,
|
||||
issuer: process.env.IDS4_ISSUER,
|
||||
}),
|
||||
DuendeIDS6Provider({
|
||||
clientId: "interactive.confidential",
|
||||
clientSecret: "secret",
|
||||
issuer: "https://demo.duendesoftware.com",
|
||||
}),
|
||||
DiscordProvider({
|
||||
clientId: process.env.DISCORD_ID,
|
||||
clientSecret: process.env.DISCORD_SECRET,
|
||||
}),
|
||||
AzureADProvider({
|
||||
clientId: process.env.AZURE_AD_CLIENT_ID,
|
||||
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
|
||||
tenantId: process.env.AZURE_AD_TENANT_ID,
|
||||
profilePhotoSize: 48,
|
||||
}),
|
||||
SpotifyProvider({
|
||||
clientId: process.env.SPOTIFY_ID,
|
||||
clientSecret: process.env.SPOTIFY_SECRET,
|
||||
}),
|
||||
CognitoProvider({
|
||||
clientId: process.env.COGNITO_ID,
|
||||
clientSecret: process.env.COGNITO_SECRET,
|
||||
issuer: process.env.COGNITO_ISSUER,
|
||||
}),
|
||||
Okta({
|
||||
clientId: process.env.OKTA_ID,
|
||||
clientSecret: process.env.OKTA_SECRET,
|
||||
issuer: process.env.OKTA_ISSUER,
|
||||
}),
|
||||
SlackProvider({
|
||||
clientId: process.env.SLACK_ID,
|
||||
clientSecret: process.env.SLACK_SECRET,
|
||||
}),
|
||||
AzureB2C({
|
||||
clientId: process.env.AZURE_B2C_ID,
|
||||
clientSecret: process.env.AZURE_B2C_SECRET,
|
||||
tenantId: process.env.AZURE_B2C_TENANT_ID,
|
||||
primaryUserFlow: process.env.AZURE_B2C_PRIMARY_USER_FLOW,
|
||||
}),
|
||||
OsuProvider({
|
||||
clientId: process.env.OSU_CLIENT_ID,
|
||||
clientSecret: process.env.OSU_CLIENT_SECRET,
|
||||
}),
|
||||
AppleProvider({
|
||||
clientId: process.env.APPLE_ID,
|
||||
clientSecret: process.env.APPLE_SECRET,
|
||||
}),
|
||||
PatreonProvider({
|
||||
clientId: process.env.PATREON_ID,
|
||||
clientSecret: process.env.PATREON_SECRET,
|
||||
}),
|
||||
TraktProvider({
|
||||
clientId: process.env.TRAKT_ID,
|
||||
clientSecret: process.env.TRAKT_SECRET,
|
||||
}),
|
||||
WorkOSProvider({
|
||||
clientId: process.env.WORKOS_ID,
|
||||
clientSecret: process.env.WORKOS_SECRET,
|
||||
}),
|
||||
BoxyHQSAMLProvider({
|
||||
issuer: process.env.BOXYHQSAML_ISSUER ?? "https://example.com",
|
||||
clientId: process.env.BOXYHQSAML_ID,
|
||||
clientSecret: process.env.BOXYHQSAML_SECRET,
|
||||
}),
|
||||
WikimediaProvider({
|
||||
clientId: process.env.WIKIMEDIA_ID,
|
||||
clientSecret: process.env.WIKIMEDIA_SECRET,
|
||||
}),
|
||||
VkProvider({
|
||||
clientId: process.env.VK_ID,
|
||||
clientSecret: process.env.VK_SECRET
|
||||
}),
|
||||
],
|
||||
debug: true,
|
||||
theme: {
|
||||
colorScheme: "auto",
|
||||
logo: "https://next-auth.js.org/img/logo/logo-sm.png",
|
||||
brandColor: "#1786fb",
|
||||
// Add an adapter you want to test here.
|
||||
const adapters = {
|
||||
prisma() {
|
||||
const client = globalThis.prisma || new PrismaClient()
|
||||
if (process.env.NODE_ENV !== "production") globalThis.prisma = client
|
||||
return PrismaAdapter(client)
|
||||
},
|
||||
typeorm() {
|
||||
return TypeORMLegacyAdapter({
|
||||
type: "sqlite",
|
||||
name: "next-auth-test-memory",
|
||||
database: "./typeorm/dev.db",
|
||||
synchronize: true,
|
||||
})
|
||||
},
|
||||
fauna() {
|
||||
const client =
|
||||
globalThis.fauna ||
|
||||
new FaunaClient({
|
||||
secret: process.env.FAUNA_SECRET,
|
||||
domain: process.env.FAUNA_DOMAIN,
|
||||
})
|
||||
if (process.env.NODE_ENV !== "production") global.fauna = client
|
||||
return FaunaAdapter(client)
|
||||
},
|
||||
noop() {
|
||||
return undefined
|
||||
},
|
||||
}
|
||||
|
||||
export const authOptions: NextAuthOptions = {
|
||||
adapter: adapters.noop(),
|
||||
debug: true,
|
||||
theme: {
|
||||
logo: "https://next-auth.js.org/img/logo/logo-sm.png",
|
||||
brandColor: "#1786fb",
|
||||
},
|
||||
providers: [
|
||||
Credentials({
|
||||
credentials: { password: { label: "Password", type: "password" } },
|
||||
async authorize(credentials) {
|
||||
if (credentials.password !== "pw") return null
|
||||
return { name: "Fill Murray", email: "bill@fillmurray.com", image: "https://www.fillmurray.com/64/64" }
|
||||
},
|
||||
}),
|
||||
Apple({ clientId: process.env.APPLE_ID, clientSecret: process.env.APPLE_SECRET }),
|
||||
Auth0({ clientId: process.env.AUTH0_ID, clientSecret: process.env.AUTH0_SECRET, issuer: process.env.AUTH0_ISSUER }),
|
||||
AzureAD({ clientId: process.env.AZURE_AD_CLIENT_ID, clientSecret: process.env.AZURE_AD_CLIENT_SECRET, tenantId: process.env.AZURE_AD_TENANT_ID }),
|
||||
AzureB2C({ clientId: process.env.AZURE_B2C_ID, clientSecret: process.env.AZURE_B2C_SECRET, issuer: process.env.AZURE_B2C_ISSUER }),
|
||||
BoxyHQSAML({ issuer: "https://jackson-demo.boxyhq.com", clientId: "tenant=boxyhq.com&product=saml-demo.boxyhq.com", clientSecret: "dummy" }),
|
||||
Cognito({ clientId: process.env.COGNITO_ID, clientSecret: process.env.COGNITO_SECRET, issuer: process.env.COGNITO_ISSUER }),
|
||||
Discord({ clientId: process.env.DISCORD_ID, clientSecret: process.env.DISCORD_SECRET }),
|
||||
DuendeIDS6({ clientId: "interactive.confidential", clientSecret: "secret", issuer: "https://demo.duendesoftware.com" }),
|
||||
Facebook({ clientId: process.env.FACEBOOK_ID, clientSecret: process.env.FACEBOOK_SECRET }),
|
||||
Foursquare({ clientId: process.env.FOURSQUARE_ID, clientSecret: process.env.FOURSQUARE_SECRET }),
|
||||
Freshbooks({ clientId: process.env.FRESHBOOKS_ID, clientSecret: process.env.FRESHBOOKS_SECRET }),
|
||||
GitHub({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET }),
|
||||
Gitlab({ clientId: process.env.GITLAB_ID, clientSecret: process.env.GITLAB_SECRET }),
|
||||
Google({ clientId: process.env.GOOGLE_ID, clientSecret: process.env.GOOGLE_SECRET }),
|
||||
IDS4({ clientId: process.env.IDS4_ID, clientSecret: process.env.IDS4_SECRET, issuer: process.env.IDS4_ISSUER }),
|
||||
Instagram({ clientId: process.env.INSTAGRAM_ID, clientSecret: process.env.INSTAGRAM_SECRET }),
|
||||
Keycloak({ clientId: process.env.KEYCLOAK_ID, clientSecret: process.env.KEYCLOAK_SECRET, issuer: process.env.KEYCLOAK_ISSUER }),
|
||||
Line({ clientId: process.env.LINE_ID, clientSecret: process.env.LINE_SECRET }),
|
||||
LinkedIn({ clientId: process.env.LINKEDIN_ID, clientSecret: process.env.LINKEDIN_SECRET }),
|
||||
Mailchimp({ clientId: process.env.MAILCHIMP_ID, clientSecret: process.env.MAILCHIMP_SECRET }),
|
||||
Okta({ clientId: process.env.OKTA_ID, clientSecret: process.env.OKTA_SECRET, issuer: process.env.OKTA_ISSUER }),
|
||||
Osu({ clientId: process.env.OSU_CLIENT_ID, clientSecret: process.env.OSU_CLIENT_SECRET }),
|
||||
Patreon({ clientId: process.env.PATREON_ID, clientSecret: process.env.PATREON_SECRET }),
|
||||
Slack({ clientId: process.env.SLACK_ID, clientSecret: process.env.SLACK_SECRET }),
|
||||
Spotify({ clientId: process.env.SPOTIFY_ID, clientSecret: process.env.SPOTIFY_SECRET }),
|
||||
Trakt({ clientId: process.env.TRAKT_ID, clientSecret: process.env.TRAKT_SECRET }),
|
||||
Twitch({ clientId: process.env.TWITCH_ID, clientSecret: process.env.TWITCH_SECRET }),
|
||||
Twitter({ version: "2.0", clientId: process.env.TWITTER_ID, clientSecret: process.env.TWITTER_SECRET }),
|
||||
TwitterLegacy({ clientId: process.env.TWITTER_LEGACY_ID, clientSecret: process.env.TWITTER_LEGACY_SECRET }),
|
||||
Vk({ clientId: process.env.VK_ID, clientSecret: process.env.VK_SECRET }),
|
||||
Wikimedia({ clientId: process.env.WIKIMEDIA_ID, clientSecret: process.env.WIKIMEDIA_SECRET }),
|
||||
WorkOS({ clientId: process.env.WORKOS_ID, clientSecret: process.env.WORKOS_SECRET }),
|
||||
],
|
||||
}
|
||||
|
||||
if (authOptions.adapter) {
|
||||
authOptions.providers.unshift(
|
||||
// NOTE: You can start a fake e-mail server with `pnpm email`
|
||||
// and then go to `http://localhost:1080` in the browser
|
||||
Email({ server: "smtp://127.0.0.1:1025?tls.rejectUnauthorized=false" })
|
||||
)
|
||||
}
|
||||
|
||||
export default NextAuth(authOptions)
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
import { getToken } from "next-auth/jwt"
|
||||
|
||||
export default async (req, res) => {
|
||||
const token = await getToken({ req, secret: process.env.SECRET })
|
||||
const token = await getToken({ req })
|
||||
res.send(JSON.stringify(token, null, 2))
|
||||
}
|
||||
|
||||
@@ -3,9 +3,14 @@ import { withAuth } from "next-auth/middleware"
|
||||
// More on how NextAuth.js middleware works: https://next-auth.js.org/configuration/nextjs#middleware
|
||||
export default withAuth({
|
||||
callbacks: {
|
||||
authorized: ({ req, token }) =>
|
||||
// /admin requires admin role, but /me only requires the user to be logged in.
|
||||
req.nextUrl.pathname !== "/admin" || token?.userRole === "admin",
|
||||
authorized({ req, token }) {
|
||||
// `/admin` requires admin role
|
||||
if (req.nextUrl.pathname === "/admin") {
|
||||
return token?.userRole === "admin"
|
||||
}
|
||||
// `/me` only requires the user to be logged in
|
||||
return !!token
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@ import { getToken } from "next-auth/jwt"
|
||||
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
const secret = process.env.NEXTAUTH_SECRET
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
const token = await getToken({ req, secret })
|
||||
// If you don't have the NEXTAUTH_SECRET environment variable set,
|
||||
// you will have to pass your secret as `secret` to `getToken`
|
||||
const token = await getToken({ req })
|
||||
res.send(JSON.stringify(token, null, 2))
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"eslint-plugin-svelte3": "^3.2.1",
|
||||
"prettier": "^2.5.1",
|
||||
"prettier-plugin-svelte": "^2.5.0",
|
||||
"svelte": "^3.44.0",
|
||||
"svelte": "^3.49.0",
|
||||
"svelte-check": "^2.2.6",
|
||||
"svelte-preprocess": "^4.10.1",
|
||||
"tslib": "^2.3.1",
|
||||
|
||||
@@ -1617,10 +1617,10 @@ svelte-preprocess@^4.0.0, svelte-preprocess@^4.10.1:
|
||||
sorcery "^0.10.0"
|
||||
strip-indent "^3.0.0"
|
||||
|
||||
svelte@^3.44.0:
|
||||
version "3.46.4"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.46.4.tgz#0c46bc4a3e20a2617a1b7dc43a722f9d6c084a38"
|
||||
integrity sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg==
|
||||
svelte@^3.49.0:
|
||||
version "3.49.0"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.49.0.tgz#5baee3c672306de1070c3b7888fc2204e36a4029"
|
||||
integrity sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA==
|
||||
|
||||
table@^6.0.9:
|
||||
version "6.8.0"
|
||||
|
||||
@@ -177,13 +177,11 @@ If you do not define the options, NextAuth.js will use the default values for th
|
||||
#### wrap middleware
|
||||
|
||||
```ts title="middleware.ts"
|
||||
import type { NextRequest } from "next/server"
|
||||
import type { JWT } from "next-auth/jwt"
|
||||
import { withAuth } from "next-auth/middleware"
|
||||
|
||||
export default withAuth(
|
||||
// `withAuth` can augment your Request with the user's token.
|
||||
function middleware(req: NextRequest & { nextauth: { token: JWT | null } }) {
|
||||
// `withAuth` augments your `Request` with the user's token.
|
||||
function middleware(req) {
|
||||
console.log(req.nextauth.token)
|
||||
},
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ Without these people, the project could not have become one of the most used aut
|
||||
- [Balázs Orbán](https://github.com/balazsorban44) - **Lead Maintainer**
|
||||
- [Nico Domino](https://github.com/ndom91) - Maintainer (Documentation, Core)
|
||||
- [Lluis Agusti](https://github.com/lluia) - Maintainer (Documentation, Testing, TypeScript)
|
||||
- [Thang Huu Vu](https://github.com/ThangHuuVu) - Maintainer (Core, TypeScript)
|
||||
|
||||
## Special thanks
|
||||
|
||||
|
||||
@@ -61,17 +61,20 @@ There should also be further details logged when this occurs, such as the error
|
||||
|
||||
### Signin / Callback
|
||||
|
||||
#### GET_AUTHORIZATION_URL_ERROR
|
||||
|
||||
This error can occur when we cannot get the OAuth v1 request token and generate the authorization URL.
|
||||
|
||||
Please double check your OAuth v1 provider settings, especially the OAuth token and OAuth token secret.
|
||||
|
||||
#### SIGNIN_OAUTH_ERROR
|
||||
|
||||
This error can occur in one of a few places, first during the redirect to the authorization URL of the provider. Next, in the signin flow while creating the PKCE code verifier. Finally, during the generation of the CSRF Token hash in the internal state during signin.
|
||||
This error occurs during the redirection to the authorization URL of the OAuth provider. Possible causes:
|
||||
|
||||
Please check your OAuth provider settings and make sure your URLs and other options are correctly set on the provider side.
|
||||
1. Cookie handling
|
||||
Either PKCE code verifier or the generation of the CSRF token hash in the internal state failed.
|
||||
|
||||
If set, check your [`cookies` configuration](/configuration/options#cookies), and make sure the browser is not blocking/restricting cookies.
|
||||
|
||||
2. OAuth misconfiguration
|
||||
|
||||
Please check your OAuth provider and make sure your URLs and other options are correctly set.
|
||||
|
||||
If you are using an OAuth v1 provider, check your OAuth v1 provider settings, especially the OAuth token and OAuth token secret.
|
||||
|
||||
#### CALLBACK_OAUTH_ERROR
|
||||
|
||||
@@ -151,12 +154,6 @@ This error occurs when there was an issue deleting the session from the database
|
||||
|
||||
### Other
|
||||
|
||||
#### SEND_VERIFICATION_EMAIL_ERROR
|
||||
|
||||
This error occurs when the Email Authentication Provider is unable to send an email.
|
||||
|
||||
Check your mail server configuration.
|
||||
|
||||
#### MISSING_NEXTAUTH_API_ROUTE_ERROR
|
||||
|
||||
This error happens when `[...nextauth].js` file is not found inside `pages/api/auth`.
|
||||
|
||||
@@ -63,17 +63,32 @@ _If you use a custom credentials provider user accounts will not be persisted in
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Can I use NextAuth.js with a website that does not use Next.js?</h3>
|
||||
<h3 style={{display:"inline-block"}}>Can I use NextAuth.js with a framework different than Next.js?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
NextAuth.js is designed for use with Next.js and Serverless.
|
||||
NextAuth.js was originally designed for use with Next.js and Serverless. However, today you could use the NextAuth.js core with any other framework. Checkout the examples for <a href="https://github.com/nextauthjs/next-auth/tree/main/apps/example-gatsby" target="_blank">Gatsby</a> and <a href="https://github.com/nextauthjs/next-auth/tree/main/apps/playground-sveltekit" target="_blank">SvelteKit</a>. If you would add another integration with other frameworks, feel free to work on it and send a pull request. Make sure to check if there's any on-going work before open a new issue.
|
||||
|
||||
If you are using a different framework for your website, you can create a website that handles sign in with Next.js and then access those sessions on a website that does not use Next.js as long as the websites are on the same domain.
|
||||
</p>
|
||||
</details>
|
||||
|
||||
If you use NextAuth.js on a website with a different subdomain then the rest of your website (e.g. `auth.example.com` vs `www.example.com`) you will need to set a custom cookie domain policy for the Session Token cookie. (See also: [Cookies](/configuration/options#cookies))
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Can session generated by NextAuth.js be used by another website?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
NextAuth.js does not currently support automatically signing into sites on different top level domains (e.g. `www.example.com` vs `www.example.org`) using a single session.
|
||||
**Same domain**: you can create a website that handles sign-in with NextAuth.js and then access those sessions on a website that does not use NextAuth.js as long as the websites are on the same domain.
|
||||
|
||||
**Same root domain, different subdomains**: If you use NextAuth.js on a website with a different subdomain than the rest of your website (e.g. `auth.example.com` vs. `www.example.com`) you will need to set a custom cookie domain policy for the Session Token cookie. (See also: [Cookies](/configuration/options#cookies)).
|
||||
|
||||
:::warning
|
||||
Changing the default cookies domain policy is advanced and can lead to security issues if done correctly. Make sure you're aware of the security implication before proceeding.
|
||||
:::
|
||||
|
||||
A working example can be found at <a href="https://github.com/vercel/examples/tree/main/solutions/subdomain-auth" target="_blank">this example repo</a>.
|
||||
|
||||
**Different root domains**: NextAuth.js does not currently support automatically signing into sites on different top-level domains (e.g. `www.example.com` vs. `www.example.org`) using a single session.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
@@ -148,13 +148,9 @@ Because of how `_app` is written, it won't unnecessarily contact the `/api/auth/
|
||||
|
||||
More information can be found in the following [GitHub Issue](https://github.com/nextauthjs/next-auth/issues/1210).
|
||||
|
||||
### NextAuth.js + React-Query
|
||||
### NextAuth.js + React Query
|
||||
|
||||
There is also an alternative client-side API library based upon [`react-query`](https://www.npmjs.com/package/react-query) available under [`nextauthjs/react-query`](https://github.com/nextauthjs/react-query).
|
||||
|
||||
If you use `react-query` in your project already, you can leverage it with NextAuth.js to handle the client-side session management for you as well. This replaces NextAuth.js's native `useSession` and `SessionProvider` from `next-auth/react`.
|
||||
|
||||
See repository [`README`](https://github.com/nextauthjs/react-query) for more details.
|
||||
You can create your own session management solution using data fetching libraries like [React Query](https://tanstack.com/query/v4/docs/adapters/react-query) or [SWR](https://swr.vercel.app). You can use the [original implementation of `@next-auth/react-query`](https://github.com/nextauthjs/react-query) and look at the [`next-auth/react` source code](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/react/index.tsx) as a starting point.
|
||||
|
||||
---
|
||||
|
||||
@@ -531,4 +527,4 @@ export default function App({
|
||||
</SessionProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
@@ -61,8 +61,9 @@ You can protect server side rendered pages using the `unstable_getServerSession`
|
||||
You need to add this to every server rendered page you want to protect. Be aware, `unstable_getServerSession` takes slightly different arguments than the method it is replacing, `getSession`.
|
||||
|
||||
```js title="pages/server-side-example.js"
|
||||
import { useSession, unstable_getServerSession } from "next-auth/next"
|
||||
import { unstable_getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "./api/auth/[...nextauth]"
|
||||
import { useSession } from "next-auth/react"
|
||||
|
||||
export default function Page() {
|
||||
const { data: session } = useSession()
|
||||
@@ -144,10 +145,9 @@ If you are using JSON Web Tokens you can use the `getToken()` helper to access t
|
||||
// This is an example of how to read a JSON Web Token from an API route
|
||||
import { getToken } from "next-auth/jwt"
|
||||
|
||||
const secret = process.env.SECRET
|
||||
|
||||
export default async (req, res) => {
|
||||
const token = await getToken({ req, secret })
|
||||
// If you don't have NEXTAUTH_SECRET set, you will have to pass your secret as `secret` to `getToken`
|
||||
const token = await getToken({ req })
|
||||
if (token) {
|
||||
// Signed in
|
||||
console.log("JSON Web Token", JSON.stringify(token, null, 2))
|
||||
|
||||
@@ -37,6 +37,12 @@ Twitter OAuth 2.0 is currently in beta as certain changes might still be necessa
|
||||
|
||||
Some APIs are still experimental; they may be changed or removed in the future. Use at your own risk.
|
||||
|
||||
#### DEBUG_ENABLED
|
||||
|
||||
You have enabled the `debug` option. It is meant for development only, to help you catch issues in your authentication flow and you should consider removing this option when deploying to production. One way of only allowing debugging while not in production is to set `debug: process.env.NODE_ENV !== "production"`, so you can commit this without needing to change the value.
|
||||
|
||||
If you want to log debug messages during production anyway, we recommend setting the [`logger` option](/configuration/options#logger) with proper sanitization of potentially sensitive user information.
|
||||
|
||||
## Adapter
|
||||
|
||||
### ADAPTER_TYPEORM_UPDATING_ENTITIES
|
||||
|
||||
21
package.json
21
package.json
@@ -11,16 +11,16 @@
|
||||
"test": "turbo run test --concurrency=1 --filter=!@next-auth/pouchdb-adapter --filter=!next-auth-* --filter=[HEAD^1]",
|
||||
"setup": "turbo run setup",
|
||||
"dev": "pnpm dev:app",
|
||||
"email": "cd apps/dev && pnpm email",
|
||||
"dev:app": "turbo run dev --parallel --no-deps --no-cache --filter=next-auth-app",
|
||||
"dev:docs": "turbo run dev --parallel --no-deps --no-cache --filter=next-auth-docs",
|
||||
"version:pr": "node ./config/version-pr",
|
||||
"release": "ts-node scripts/release"
|
||||
"release": "release"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/core": "^1.6.0",
|
||||
"@commitlint/parse": "16.0.0",
|
||||
"@balazsorban/monorepo-release": "0.0.4",
|
||||
"@types/node": "^17.0.25",
|
||||
"@types/semver": "7.3.9",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
||||
"@typescript-eslint/parser": "^4.33.0",
|
||||
"eslint": "^7.32.0",
|
||||
@@ -30,15 +30,10 @@
|
||||
"eslint-plugin-jest": "^25.3.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"git-log-parser": "1.2.0",
|
||||
"husky": "^7.0.4",
|
||||
"prettier": "2.4.1",
|
||||
"pretty-quick": "^3.1.2",
|
||||
"semver": "7.3.5",
|
||||
"stream-to-array": "2.3.0",
|
||||
"ts-node": "10.8.2",
|
||||
"turbo": "1.3.1",
|
||||
"type-fest": "2.16.0",
|
||||
"typescript": "^4.5.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -47,7 +42,15 @@
|
||||
},
|
||||
"prettier": {
|
||||
"semi": false,
|
||||
"singleQuote": false
|
||||
"singleQuote": false,
|
||||
"overrides": [
|
||||
{
|
||||
"files": "apps/dev/pages/api/auth/[...nextauth].ts",
|
||||
"options": {
|
||||
"printWidth": 150
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"eslintConfig": {
|
||||
"parser": "@typescript-eslint/parser",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/dgraph-adapter",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"description": "Dgraph adapter for next-auth.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -31,7 +31,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"next-auth": "workspace:*"
|
||||
"next-auth": "^4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next-auth/adapter-test": "workspace:^0.0.0",
|
||||
@@ -50,4 +50,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@next-auth/dynamodb-adapter",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"description": "AWS DynamoDB adapter for next-auth.",
|
||||
"keywords": [
|
||||
"next-auth",
|
||||
@@ -32,7 +32,7 @@
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"@aws-sdk/lib-dynamodb": "^3.36.1",
|
||||
"next-auth": "workspace:*"
|
||||
"next-auth": "^4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-dynamodb": "^3.36.1",
|
||||
@@ -43,4 +43,4 @@
|
||||
"jest": "^27.4.3",
|
||||
"next-auth": "workspace:*"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/fauna-adapter",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"description": "Fauna Adapter for NextAuth",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -41,7 +41,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"faunadb": "^4.3.0",
|
||||
"next-auth": "workspace:*"
|
||||
"next-auth": "^4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fauna-labs/fauna-schema-migrate": "^2.1.3",
|
||||
@@ -54,4 +54,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/firebase-adapter",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"description": "Firebase adapter for next-auth.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"firebase": "^9.7.0",
|
||||
"next-auth": "workspace:*"
|
||||
"next-auth": "^4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next-auth/adapter-test": "workspace:^0.0.0",
|
||||
@@ -43,4 +43,4 @@
|
||||
"jest": "^27.4.3",
|
||||
"next-auth": "workspace:*"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/mongodb-adapter",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"description": "mongoDB adapter for next-auth.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -32,7 +32,7 @@
|
||||
],
|
||||
"peerDependencies": {
|
||||
"mongodb": "^4.1.1",
|
||||
"next-auth": "workspace:*"
|
||||
"next-auth": "^4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next-auth/adapter-test": "workspace:^0.0.0",
|
||||
@@ -44,4 +44,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/neo4j-adapter",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"description": "neo4j adapter for next-auth.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -34,7 +34,7 @@
|
||||
],
|
||||
"peerDependencies": {
|
||||
"neo4j-driver": "^4.0.0",
|
||||
"next-auth": "workspace:*"
|
||||
"next-auth": "^4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next-auth/adapter-test": "workspace:^0.0.0",
|
||||
@@ -50,4 +50,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/pouchdb-adapter",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"description": "PouchDB adapter for next-auth.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -30,7 +30,7 @@
|
||||
"dist"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"next-auth": "workspace:*",
|
||||
"next-auth": "^3",
|
||||
"pouchdb": "^7.2.2",
|
||||
"pouchdb-find": "^7.2.2"
|
||||
},
|
||||
@@ -51,4 +51,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/prisma-adapter",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"description": "Prisma adapter for next-auth.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -37,7 +37,7 @@
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@prisma/client": ">=2.26.0 || >=3",
|
||||
"next-auth": "workspace:*"
|
||||
"next-auth": "^4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next-auth/adapter-test": "workspace:^0.0.0",
|
||||
@@ -51,4 +51,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/upstash-redis-adapter",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"description": "Upstash adapter for next-auth. It uses Upstash's connectionless (HTTP based) Redis client.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -31,7 +31,7 @@
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@upstash/redis": "^1.0.1",
|
||||
"next-auth": "workspace:*"
|
||||
"next-auth": "^4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next-auth/adapter-test": "workspace:^0.0.0",
|
||||
@@ -39,6 +39,7 @@
|
||||
"@types/uuid": "^8.3.3",
|
||||
"@upstash/redis": "^1.0.1",
|
||||
"dotenv": "^10.0.0",
|
||||
"isomorphic-fetch": "3.0.0",
|
||||
"jest": "^27.4.3",
|
||||
"next-auth": "workspace:*"
|
||||
},
|
||||
@@ -48,4 +49,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import "isomorphic-fetch"
|
||||
import { Redis } from "@upstash/redis"
|
||||
import { runBasicTests } from "@next-auth/adapter-test"
|
||||
import { hydrateDates, UpstashRedisAdapter } from "../src"
|
||||
@@ -7,41 +8,42 @@ if (!process.env.UPSTASH_REDIS_URL || !process.env.UPSTASH_REDIS_KEY) {
|
||||
test("Skipping UpstashRedisAdapter tests, since required environment variables aren't available", () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
} else {
|
||||
const client = new Redis({
|
||||
url: process.env.UPSTASH_REDIS_URL,
|
||||
token: process.env.UPSTASH_REDIS_KEY,
|
||||
})
|
||||
|
||||
runBasicTests({
|
||||
adapter: UpstashRedisAdapter(client, { baseKeyPrefix: "testApp:" }),
|
||||
db: {
|
||||
async user(id: string) {
|
||||
const data = await client.get<object>(`testApp:user:${id}`)
|
||||
if (!data) return null
|
||||
return hydrateDates(data)
|
||||
},
|
||||
async account({ provider, providerAccountId }) {
|
||||
const data = await client.get<object>(
|
||||
`testApp:user:account:${provider}:${providerAccountId}`
|
||||
)
|
||||
if (!data) return null
|
||||
return hydrateDates(data)
|
||||
},
|
||||
async session(sessionToken) {
|
||||
const data = await client.get<object>(
|
||||
`testApp:user:session:${sessionToken}`
|
||||
)
|
||||
if (!data) return null
|
||||
return hydrateDates(data)
|
||||
},
|
||||
async verificationToken(where) {
|
||||
const data = await client.get<object>(
|
||||
`testApp:user:token:${where.identifier}`
|
||||
)
|
||||
if (!data) return null
|
||||
return hydrateDates(data)
|
||||
},
|
||||
},
|
||||
})
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const client = new Redis({
|
||||
url: process.env.UPSTASH_REDIS_URL,
|
||||
token: process.env.UPSTASH_REDIS_KEY,
|
||||
})
|
||||
|
||||
runBasicTests({
|
||||
adapter: UpstashRedisAdapter(client, { baseKeyPrefix: "testApp:" }),
|
||||
db: {
|
||||
async user(id: string) {
|
||||
const data = await client.get<object>(`testApp:user:${id}`)
|
||||
if (!data) return null
|
||||
return hydrateDates(data)
|
||||
},
|
||||
async account({ provider, providerAccountId }) {
|
||||
const data = await client.get<object>(
|
||||
`testApp:user:account:${provider}:${providerAccountId}`
|
||||
)
|
||||
if (!data) return null
|
||||
return hydrateDates(data)
|
||||
},
|
||||
async session(sessionToken) {
|
||||
const data = await client.get<object>(
|
||||
`testApp:user:session:${sessionToken}`
|
||||
)
|
||||
if (!data) return null
|
||||
return hydrateDates(data)
|
||||
},
|
||||
async verificationToken(where) {
|
||||
const data = await client.get<object>(
|
||||
`testApp:user:token:${where.identifier}`
|
||||
)
|
||||
if (!data) return null
|
||||
return hydrateDates(data)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "next-auth",
|
||||
"version": "4.10.0",
|
||||
"version": "4.10.2",
|
||||
"description": "Authentication for Next.js",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth.git",
|
||||
@@ -8,7 +8,8 @@
|
||||
"contributors": [
|
||||
"Balázs Orbán <info@balazsorban.com>",
|
||||
"Nico Domino <yo@ndo.dev>",
|
||||
"Lluis Agusti <hi@llu.lu>"
|
||||
"Lluis Agusti <hi@llu.lu>",
|
||||
"Thang Huu Vu <thvu@hey.com>"
|
||||
],
|
||||
"main": "index.js",
|
||||
"module": "index.js",
|
||||
@@ -138,4 +139,4 @@
|
||||
"**/tests",
|
||||
"**/__tests__"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ test("when the fetch fails it'll throw a client fetch error", async () => {
|
||||
await waitFor(() => {
|
||||
expect(logger.error).toHaveBeenCalledTimes(1)
|
||||
expect(logger.error).toBeCalledWith("CLIENT_FETCH_ERROR", {
|
||||
path: "csrf",
|
||||
url: "/api/auth/csrf",
|
||||
error: new SyntaxError("Unexpected token s in JSON at position 0"),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -57,7 +57,7 @@ test("when failing to fetch the providers, it'll log the error", async () => {
|
||||
await waitFor(() => {
|
||||
expect(logger.error).toHaveBeenCalledTimes(1)
|
||||
expect(logger.error).toBeCalledWith("CLIENT_FETCH_ERROR", {
|
||||
path: "providers",
|
||||
url: "/api/auth/providers",
|
||||
error: new SyntaxError("Unexpected token s in JSON at position 0"),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -71,7 +71,7 @@ test("if there's an error fetching the session, it should log it", async () => {
|
||||
await waitFor(() => {
|
||||
expect(logger.error).toHaveBeenCalledTimes(1)
|
||||
expect(logger.error).toBeCalledWith("CLIENT_FETCH_ERROR", {
|
||||
path: "session",
|
||||
url: "/api/auth/session",
|
||||
error: new SyntaxError("Unexpected token S in JSON at position 0"),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -256,7 +256,7 @@ test("when it fails to fetch the providers, it redirected back to signin page",
|
||||
expect(logger.error).toHaveBeenCalledTimes(1)
|
||||
expect(logger.error).toBeCalledWith("CLIENT_FETCH_ERROR", {
|
||||
error: "Error when retrieving providers",
|
||||
path: "providers",
|
||||
url: "/api/auth/providers",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -35,20 +35,17 @@ export async function fetchData<T = any>(
|
||||
logger: LoggerInstance,
|
||||
{ ctx, req = ctx?.req }: CtxOrReq = {}
|
||||
): Promise<T | null> {
|
||||
const url = `${apiBaseUrl(__NEXTAUTH)}/${path}`
|
||||
try {
|
||||
const options = req?.headers.cookie
|
||||
? { headers: { cookie: req.headers.cookie } }
|
||||
: {}
|
||||
const res = await fetch(`${apiBaseUrl(__NEXTAUTH)}/${path}`, options)
|
||||
const res = await fetch(url, options)
|
||||
const data = await res.json()
|
||||
if (!res.ok) throw data
|
||||
return Object.keys(data).length > 0 ? data : null // Return null if data empty
|
||||
} catch (error) {
|
||||
logger.error("CLIENT_FETCH_ERROR", {
|
||||
error: error as Error,
|
||||
path,
|
||||
...(req ? { header: req.headers } : {}),
|
||||
})
|
||||
logger.error("CLIENT_FETCH_ERROR", { error: error as Error, url })
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,8 +90,8 @@ export async function NextAuthHandler<
|
||||
|
||||
const assertionResult = assertConfig({ options: userOptions, req })
|
||||
|
||||
if (typeof assertionResult === "string") {
|
||||
logger.warn(assertionResult)
|
||||
if (Array.isArray(assertionResult)) {
|
||||
assertionResult.forEach(logger.warn)
|
||||
} else if (assertionResult instanceof Error) {
|
||||
// Bail out early if there's an error in the user config
|
||||
const { pages, theme } = userOptions
|
||||
|
||||
@@ -3,7 +3,7 @@ import logger from "../utils/logger"
|
||||
import parseUrl from "../utils/parse-url"
|
||||
import { adapterErrorHandler, eventsErrorHandler } from "./errors"
|
||||
import parseProviders from "./lib/providers"
|
||||
import createSecret from "./lib/utils"
|
||||
import { createSecret } from "./lib/utils"
|
||||
import * as cookie from "./lib/cookie"
|
||||
import * as jwt from "../jwt"
|
||||
import { defaultCallbacks } from "./lib/default-callbacks"
|
||||
|
||||
@@ -9,8 +9,9 @@ import {
|
||||
import parseUrl from "../../utils/parse-url"
|
||||
import { defaultCookies } from "./cookie"
|
||||
|
||||
import type { NextAuthHandlerParams, RequestInternal } from ".."
|
||||
import type { RequestInternal } from ".."
|
||||
import type { WarningCode } from "../../utils/logger"
|
||||
import type { NextAuthOptions } from "../types"
|
||||
|
||||
type ConfigError =
|
||||
| MissingAPIRoute
|
||||
@@ -19,7 +20,7 @@ type ConfigError =
|
||||
| MissingAuthorize
|
||||
| MissingAdapter
|
||||
|
||||
let twitterWarned = false
|
||||
let warned = false
|
||||
|
||||
function isValidHttpUrl(url: string, baseUrl: string) {
|
||||
try {
|
||||
@@ -37,13 +38,28 @@ function isValidHttpUrl(url: string, baseUrl: string) {
|
||||
*
|
||||
* REVIEW: Make some of these and corresponding docs less Next.js specific?
|
||||
*/
|
||||
export function assertConfig(
|
||||
params: NextAuthHandlerParams & {
|
||||
req: RequestInternal
|
||||
}
|
||||
): ConfigError | WarningCode | undefined {
|
||||
export function assertConfig(params: {
|
||||
options: NextAuthOptions
|
||||
req: RequestInternal
|
||||
}): ConfigError | WarningCode[] {
|
||||
const { options, req } = params
|
||||
|
||||
const warnings: WarningCode[] = []
|
||||
|
||||
if (!warned) {
|
||||
if (!req.host) warnings.push("NEXTAUTH_URL")
|
||||
|
||||
// TODO: Make this throw an error in next major. This will also get rid of `NODE_ENV`
|
||||
if (!options.secret && process.env.NODE_ENV !== "production")
|
||||
warnings.push("NO_SECRET")
|
||||
|
||||
if (options.debug) warnings.push("DEBUG_ENABLED")
|
||||
}
|
||||
|
||||
if (!options.secret && process.env.NODE_ENV === "production") {
|
||||
return new MissingSecret("Please define a `secret` in production.")
|
||||
}
|
||||
|
||||
// req.query isn't defined when asserting `unstable_getServerSession` for example
|
||||
if (!req.query?.nextauth && !req.action) {
|
||||
return new MissingAPIRoute(
|
||||
@@ -51,14 +67,6 @@ export function assertConfig(
|
||||
)
|
||||
}
|
||||
|
||||
if (!options.secret) {
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
return new MissingSecret("Please define a `secret` in production.")
|
||||
} else {
|
||||
return "NO_SECRET"
|
||||
}
|
||||
}
|
||||
|
||||
const callbackUrlParam = req.query?.callbackUrl as string | undefined
|
||||
|
||||
const url = parseUrl(req.host)
|
||||
@@ -69,9 +77,6 @@ export function assertConfig(
|
||||
)
|
||||
}
|
||||
|
||||
// This is below the callbackUrlParam check because it would obscure the error
|
||||
if (!req.host) return "NEXTAUTH_URL"
|
||||
|
||||
const { callbackUrl: defaultCallbackUrl } = defaultCookies(
|
||||
options.useSecureCookies ?? url.base.startsWith("https://")
|
||||
)
|
||||
@@ -119,8 +124,10 @@ export function assertConfig(
|
||||
return new MissingAdapter("E-mail login requires an adapter.")
|
||||
}
|
||||
|
||||
if (!twitterWarned && hasTwitterOAuth2) {
|
||||
twitterWarned = true
|
||||
return "TWITTER_OAUTH_2_BETA"
|
||||
if (!warned) {
|
||||
if (hasTwitterOAuth2) warnings.push("TWITTER_OAUTH_2_BETA")
|
||||
warned = true
|
||||
}
|
||||
|
||||
return warnings
|
||||
}
|
||||
|
||||
@@ -9,9 +9,8 @@ import type { InternalOptions } from "../../types"
|
||||
export default async function email(
|
||||
identifier: string,
|
||||
options: InternalOptions<"email">
|
||||
) {
|
||||
const { url, adapter, provider, logger, callbackUrl, theme } = options
|
||||
|
||||
): Promise<string> {
|
||||
const { url, adapter, provider, callbackUrl, theme } = options
|
||||
// Generate token
|
||||
const token =
|
||||
(await provider.generateVerificationToken?.()) ??
|
||||
@@ -34,22 +33,18 @@ export default async function email(
|
||||
const params = new URLSearchParams({ callbackUrl, token, email: identifier })
|
||||
const _url = `${url}/callback/${provider.id}?${params}`
|
||||
|
||||
try {
|
||||
// Send to user
|
||||
await provider.sendVerificationRequest({
|
||||
identifier,
|
||||
token,
|
||||
expires,
|
||||
url: _url,
|
||||
provider,
|
||||
theme,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error("SEND_VERIFICATION_EMAIL_ERROR", {
|
||||
identifier,
|
||||
url,
|
||||
error: error as Error,
|
||||
})
|
||||
throw new Error("SEND_VERIFICATION_EMAIL_ERROR")
|
||||
}
|
||||
// Send to user
|
||||
await provider.sendVerificationRequest({
|
||||
identifier,
|
||||
token,
|
||||
expires,
|
||||
url: _url,
|
||||
provider,
|
||||
theme,
|
||||
})
|
||||
|
||||
return `${url}/verify-request?${new URLSearchParams({
|
||||
provider: provider.id,
|
||||
type: provider.type,
|
||||
})}`
|
||||
}
|
||||
|
||||
@@ -14,66 +14,63 @@ import type { Cookie } from "../cookie"
|
||||
*
|
||||
* [OAuth 2](https://www.oauth.com/oauth2-servers/authorization/the-authorization-request/) | [OAuth 1](https://oauth.net/core/1.0a/#auth_step2)
|
||||
*/
|
||||
export default async function getAuthorizationUrl(params: {
|
||||
export default async function getAuthorizationUrl({
|
||||
options,
|
||||
query,
|
||||
}: {
|
||||
options: InternalOptions<"oauth">
|
||||
query: RequestInternal["query"]
|
||||
}) {
|
||||
const { options, query } = params
|
||||
const { logger, provider } = options
|
||||
try {
|
||||
let params: any = {}
|
||||
let params: any = {}
|
||||
|
||||
if (typeof provider.authorization === "string") {
|
||||
const parsedUrl = new URL(provider.authorization)
|
||||
const parsedParams = Object.fromEntries(parsedUrl.searchParams.entries())
|
||||
params = { ...params, ...parsedParams }
|
||||
} else {
|
||||
params = { ...params, ...provider.authorization?.params }
|
||||
}
|
||||
|
||||
params = { ...params, ...query }
|
||||
|
||||
// Handle OAuth v1.x
|
||||
if (provider.version?.startsWith("1.")) {
|
||||
const client = oAuth1Client(options)
|
||||
const tokens = (await client.getOAuthRequestToken(params)) as any
|
||||
const url = `${
|
||||
// @ts-expect-error
|
||||
provider.authorization?.url ?? provider.authorization
|
||||
}?${new URLSearchParams({
|
||||
oauth_token: tokens.oauth_token,
|
||||
oauth_token_secret: tokens.oauth_token_secret,
|
||||
...tokens.params,
|
||||
})}`
|
||||
|
||||
logger.debug("GET_AUTHORIZATION_URL", { url })
|
||||
return { redirect: url }
|
||||
}
|
||||
|
||||
const client = await openidClient(options)
|
||||
|
||||
const authorizationParams: AuthorizationParameters = params
|
||||
const cookies: Cookie[] = []
|
||||
|
||||
const state = await createState(options)
|
||||
if (state) {
|
||||
authorizationParams.state = state.value
|
||||
cookies.push(state.cookie)
|
||||
}
|
||||
|
||||
const pkce = await createPKCE(options)
|
||||
if (pkce) {
|
||||
authorizationParams.code_challenge = pkce.code_challenge
|
||||
authorizationParams.code_challenge_method = pkce.code_challenge_method
|
||||
cookies.push(pkce.cookie)
|
||||
}
|
||||
|
||||
const url = client.authorizationUrl(authorizationParams)
|
||||
|
||||
logger.debug("GET_AUTHORIZATION_URL", { url, cookies })
|
||||
return { redirect: url, cookies }
|
||||
} catch (error) {
|
||||
logger.error("GET_AUTHORIZATION_URL_ERROR", error as Error)
|
||||
throw error
|
||||
if (typeof provider.authorization === "string") {
|
||||
const parsedUrl = new URL(provider.authorization)
|
||||
const parsedParams = Object.fromEntries(parsedUrl.searchParams.entries())
|
||||
params = { ...params, ...parsedParams }
|
||||
} else {
|
||||
params = { ...params, ...provider.authorization?.params }
|
||||
}
|
||||
|
||||
params = { ...params, ...query }
|
||||
|
||||
// Handle OAuth v1.x
|
||||
if (provider.version?.startsWith("1.")) {
|
||||
const client = oAuth1Client(options)
|
||||
const tokens = (await client.getOAuthRequestToken(params)) as any
|
||||
const url = `${
|
||||
// @ts-expect-error
|
||||
provider.authorization?.url ?? provider.authorization
|
||||
}?${new URLSearchParams({
|
||||
oauth_token: tokens.oauth_token,
|
||||
oauth_token_secret: tokens.oauth_token_secret,
|
||||
...tokens.params,
|
||||
})}`
|
||||
|
||||
logger.debug("GET_AUTHORIZATION_URL", { url, provider })
|
||||
return { redirect: url }
|
||||
}
|
||||
|
||||
const client = await openidClient(options)
|
||||
|
||||
const authorizationParams: AuthorizationParameters = params
|
||||
const cookies: Cookie[] = []
|
||||
|
||||
const state = await createState(options)
|
||||
if (state) {
|
||||
authorizationParams.state = state.value
|
||||
cookies.push(state.cookie)
|
||||
}
|
||||
|
||||
const pkce = await createPKCE(options)
|
||||
if (pkce) {
|
||||
authorizationParams.code_challenge = pkce.code_challenge
|
||||
authorizationParams.code_challenge_method = pkce.code_challenge_method
|
||||
cookies.push(pkce.cookie)
|
||||
}
|
||||
|
||||
const url = client.authorizationUrl(authorizationParams)
|
||||
|
||||
logger.debug("GET_AUTHORIZATION_URL", { url, cookies, provider })
|
||||
return { redirect: url, cookies }
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ export default async function oAuthCallback(params: {
|
||||
logger.error("OAUTH_CALLBACK_HANDLER_ERROR", {
|
||||
error,
|
||||
error_description: query?.error_description,
|
||||
body,
|
||||
providerId: provider.id,
|
||||
})
|
||||
logger.debug("OAUTH_CALLBACK_HANDLER_ERROR", { body })
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -199,10 +199,7 @@ async function getProfile({
|
||||
// all providers, so we return an empty object; the user should then be
|
||||
// redirected back to the sign up page. We log the error to help developers
|
||||
// who might be trying to debug this when configuring a new provider.
|
||||
logger.error("OAUTH_PARSE_PROFILE_ERROR", {
|
||||
error: error as Error,
|
||||
OAuthProfile,
|
||||
})
|
||||
logger.error("OAUTH_PARSE_PROFILE_ERROR", error as Error)
|
||||
return {
|
||||
profile: null,
|
||||
account: null,
|
||||
|
||||
@@ -28,7 +28,7 @@ export function hashToken(token: string, options: InternalOptions<"email">) {
|
||||
* If no secret option is specified then it creates one on the fly
|
||||
* based on options passed here. If options contains unique data, such as
|
||||
* OAuth provider secrets and database credentials it should be sufficent. If no secret provided in production, we throw an error. */
|
||||
export default function createSecret(params: {
|
||||
export function createSecret(params: {
|
||||
userOptions: NextAuthOptions
|
||||
url: InternalUrl
|
||||
}) {
|
||||
@@ -36,6 +36,7 @@ export default function createSecret(params: {
|
||||
|
||||
return (
|
||||
userOptions.secret ??
|
||||
// TODO: Remove falling back to default secret, and error in dev if one isn't provided
|
||||
createHash("sha256")
|
||||
.update(JSON.stringify({ ...url, ...userOptions }))
|
||||
.digest("hex")
|
||||
|
||||
@@ -26,7 +26,10 @@ export default async function signin(params: {
|
||||
const response = await getAuthorizationUrl({ options, query })
|
||||
return response
|
||||
} catch (error) {
|
||||
logger.error("SIGNIN_OAUTH_ERROR", { error: error as Error, provider })
|
||||
logger.error("SIGNIN_OAUTH_ERROR", {
|
||||
error: error as Error,
|
||||
providerId: provider.id,
|
||||
})
|
||||
return { redirect: `${url}/error?error=OAuthSignin` }
|
||||
}
|
||||
} else if (provider.type === "email") {
|
||||
@@ -79,18 +82,15 @@ export default async function signin(params: {
|
||||
}
|
||||
|
||||
try {
|
||||
await emailSignin(email, options)
|
||||
const redirect = await emailSignin(email, options)
|
||||
return { redirect }
|
||||
} catch (error) {
|
||||
logger.error("SIGNIN_EMAIL_ERROR", error as Error)
|
||||
logger.error("SIGNIN_EMAIL_ERROR", {
|
||||
error: error as Error,
|
||||
providerId: provider.id,
|
||||
})
|
||||
return { redirect: `${url}/error?error=EmailSignin` }
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
provider: provider.id,
|
||||
type: provider.type,
|
||||
})
|
||||
|
||||
return { redirect: `${url}/verify-request?${params}` }
|
||||
}
|
||||
return { redirect: `${url}/signin` }
|
||||
}
|
||||
|
||||
@@ -92,10 +92,14 @@ export interface NextAuthMiddlewareOptions {
|
||||
secret?: string
|
||||
}
|
||||
|
||||
// TODO: `NextMiddleware` should allow returning `void`
|
||||
// Simplify when https://github.com/vercel/next.js/pull/38625 is merged.
|
||||
type NextMiddlewareResult = ReturnType<NextMiddleware> | void // eslint-disable-line @typescript-eslint/no-invalid-void-type
|
||||
|
||||
async function handleMiddleware(
|
||||
req: NextRequest,
|
||||
options: NextAuthMiddlewareOptions | undefined,
|
||||
onSuccess?: (token: JWT | null) => Promise<any>
|
||||
onSuccess?: (token: JWT | null) => Promise<NextMiddlewareResult>
|
||||
) {
|
||||
const signInPage = options?.pages?.signIn ?? "/api/auth/signin"
|
||||
const errorPage = options?.pages?.error ?? "/api/auth/error"
|
||||
@@ -143,12 +147,21 @@ async function handleMiddleware(
|
||||
return NextResponse.redirect(signInUrl)
|
||||
}
|
||||
|
||||
export interface NextRequestWithAuth extends NextRequest {
|
||||
nextauth: { token: JWT | null }
|
||||
}
|
||||
|
||||
export type NextMiddlewareWithAuth = (
|
||||
request: NextRequestWithAuth,
|
||||
event: NextFetchEvent
|
||||
) => NextMiddlewareResult | Promise<NextMiddlewareResult>
|
||||
|
||||
export type WithAuthArgs =
|
||||
| [NextRequest]
|
||||
| [NextRequest, NextFetchEvent]
|
||||
| [NextRequest, NextAuthMiddlewareOptions]
|
||||
| [NextMiddleware]
|
||||
| [NextMiddleware, NextAuthMiddlewareOptions]
|
||||
| [NextRequestWithAuth]
|
||||
| [NextRequestWithAuth, NextFetchEvent]
|
||||
| [NextRequestWithAuth, NextAuthMiddlewareOptions]
|
||||
| [NextMiddlewareWithAuth]
|
||||
| [NextMiddlewareWithAuth, NextAuthMiddlewareOptions]
|
||||
| [NextAuthMiddlewareOptions]
|
||||
| []
|
||||
|
||||
@@ -176,9 +189,9 @@ export function withAuth(...args: WithAuthArgs) {
|
||||
if (typeof args[0] === "function") {
|
||||
const middleware = args[0]
|
||||
const options = args[1] as NextAuthMiddlewareOptions | undefined
|
||||
return async (...args: Parameters<NextMiddleware>) =>
|
||||
return async (...args: Parameters<NextMiddlewareWithAuth>) =>
|
||||
await handleMiddleware(args[0], options, async (token) => {
|
||||
;(args[0] as any).nextauth = { token }
|
||||
args[0].nextauth = { token }
|
||||
return await middleware(...args)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -19,16 +19,19 @@ export interface AzureB2CProfile extends Record<string, any> {
|
||||
|
||||
export default function AzureADB2C<P extends AzureB2CProfile>(
|
||||
options: OAuthUserConfig<P> & {
|
||||
primaryUserFlow: string
|
||||
tenantId: string
|
||||
primaryUserFlow?: string
|
||||
tenantId?: string
|
||||
}
|
||||
): OAuthConfig<P> {
|
||||
const { tenantId, primaryUserFlow } = options
|
||||
const issuer =
|
||||
options.issuer ??
|
||||
`https://${tenantId}.b2clogin.com/${tenantId}.onmicrosoft.com/${primaryUserFlow}/v2.0`
|
||||
return {
|
||||
id: "azure-ad-b2c",
|
||||
name: "Azure Active Directory B2C",
|
||||
type: "oauth",
|
||||
wellKnown: `https://${tenantId}.b2clogin.com/${tenantId}.onmicrosoft.com/${primaryUserFlow}/v2.0/.well-known/openid-configuration`,
|
||||
wellKnown: `${issuer}/.well-known/openid-configuration`,
|
||||
idToken: true,
|
||||
profile(profile) {
|
||||
return {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
/**
|
||||
* Source https://docs.github.com/en/rest/users/users#get-the-authenticated-user
|
||||
*/
|
||||
/** @see https://docs.github.com/en/rest/users/users#get-the-authenticated-user */
|
||||
export interface GithubProfile extends Record<string, any> {
|
||||
login: string
|
||||
id: number
|
||||
@@ -55,7 +53,7 @@ export interface GithubEmail extends Record<string, any> {
|
||||
email: string
|
||||
primary: boolean
|
||||
verified: boolean
|
||||
visibility: string | null
|
||||
visibility: "public" | "private"
|
||||
}
|
||||
|
||||
export default function Github<P extends GithubProfile>(
|
||||
@@ -67,29 +65,25 @@ export default function Github<P extends GithubProfile>(
|
||||
type: "oauth",
|
||||
authorization: {
|
||||
url: "https://github.com/login/oauth/authorize",
|
||||
params: { scope: "read:user+user:email" },
|
||||
params: { scope: "read:user user:email" },
|
||||
},
|
||||
token: "https://github.com/login/oauth/access_token",
|
||||
userinfo: {
|
||||
url: "https://api.github.com/user",
|
||||
async request({ client, tokens }) {
|
||||
// Get base profile
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const profile = await client.userinfo(tokens.access_token!)
|
||||
|
||||
// If user has email hidden, get their primary email from the GitHub API
|
||||
if (!profile.email) {
|
||||
const emails: GithubEmail[] = await (
|
||||
await fetch("https://api.github.com/user/emails", {
|
||||
headers: { Authorization: `token ${tokens.access_token}` },
|
||||
})
|
||||
).json()
|
||||
// If the user does not have a public email, get another via the GitHub API
|
||||
// See https://docs.github.com/en/rest/users/emails#list-public-email-addresses-for-the-authenticated-user
|
||||
const res = await fetch("https://api.github.com/user/emails", {
|
||||
headers: { Authorization: `token ${tokens.access_token}` },
|
||||
})
|
||||
|
||||
if (emails?.length > 0) {
|
||||
// Get primary email
|
||||
profile.email = emails.find((email) => email.primary)?.email
|
||||
// And if for some reason it doesn't exist, just use the first
|
||||
if (!profile.email) profile.email = emails[0].email
|
||||
if (res.ok) {
|
||||
const emails: GithubEmail[] = await res.json()
|
||||
profile.email = (emails.find((e) => e.primary) ?? emails[0]).email
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/** @type {import("src/providers").OAuthProvider} */
|
||||
/** @type {import(".").OAuthProvider} */
|
||||
export default function GitLab(options) {
|
||||
return {
|
||||
id: "gitlab",
|
||||
name: "GitLab",
|
||||
type: "oauth",
|
||||
authorization: "https://gitlab.com/oauth/authorize?scope=read_user",
|
||||
token: "https://gitlab.com/oauth/token",
|
||||
userinfo: "https://gitlab.com/api/v4/user",
|
||||
checks: ["pkce", "state"],
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.username,
|
||||
email: profile.email,
|
||||
image: profile.avatar_url,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
72
packages/next-auth/src/providers/gitlab.ts
Normal file
72
packages/next-auth/src/providers/gitlab.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface GitLabProfile extends Record<string, any> {
|
||||
id: number
|
||||
username: string
|
||||
email: string
|
||||
name: string
|
||||
state: string
|
||||
avatar_url: string
|
||||
web_url: string
|
||||
created_at: string
|
||||
bio: string
|
||||
location?: string
|
||||
public_email: string
|
||||
skype: string
|
||||
linkedin: string
|
||||
twitter: string
|
||||
website_url: string
|
||||
organization: string
|
||||
job_title: string
|
||||
pronouns: string
|
||||
bot: boolean
|
||||
work_information?: string
|
||||
followers: number
|
||||
following: number
|
||||
local_time: string
|
||||
last_sign_in_at: string
|
||||
confirmed_at: string
|
||||
theme_id: number
|
||||
last_activity_on: string
|
||||
color_scheme_id: number
|
||||
projects_limit: number
|
||||
current_sign_in_at: string
|
||||
identities: Array<{
|
||||
provider: string
|
||||
extern_uid: string
|
||||
}>
|
||||
can_create_group: boolean
|
||||
can_create_project: boolean
|
||||
two_factor_enabled: boolean
|
||||
external: boolean
|
||||
private_profile: boolean
|
||||
commit_email: string
|
||||
shared_runners_minutes_limit: number
|
||||
extra_shared_runners_minutes_limit: number
|
||||
}
|
||||
|
||||
export default function GitLab<P extends GitLabProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "gitlab",
|
||||
name: "GitLab",
|
||||
type: "oauth",
|
||||
authorization: {
|
||||
url: "https://gitlab.com/oauth/authorize",
|
||||
params: { scope: "read_user" },
|
||||
},
|
||||
token: "https://gitlab.com/oauth/token",
|
||||
userinfo: "https://gitlab.com/api/v4/user",
|
||||
checks: ["pkce", "state"],
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id.toString(),
|
||||
name: profile.name ?? profile.username,
|
||||
email: profile.email,
|
||||
image: profile.avatar_url,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
@@ -175,7 +175,11 @@ export async function getProviders() {
|
||||
export async function signIn<
|
||||
P extends RedirectableProviderType | undefined = undefined
|
||||
>(
|
||||
provider?: LiteralUnion<P extends RedirectableProviderType ? P | BuiltInProviderType : BuiltInProviderType>,
|
||||
provider?: LiteralUnion<
|
||||
P extends RedirectableProviderType
|
||||
? P | BuiltInProviderType
|
||||
: BuiltInProviderType
|
||||
>,
|
||||
options?: SignInOptions,
|
||||
authorizationParams?: SignInAuthorizationParams
|
||||
): Promise<
|
||||
@@ -224,6 +228,7 @@ export async function signIn<
|
||||
|
||||
const data = await res.json()
|
||||
|
||||
// TODO: Do not redirect for Credentials and Email providers by default in next major
|
||||
if (redirect || !isSupportingReturn) {
|
||||
const url = data.url ?? callbackUrl
|
||||
window.location.href = url
|
||||
|
||||
@@ -19,7 +19,11 @@ function hasErrorProperty(
|
||||
return !!(x as any)?.error
|
||||
}
|
||||
|
||||
export type WarningCode = "NEXTAUTH_URL" | "NO_SECRET" | "TWITTER_OAUTH_2_BETA"
|
||||
export type WarningCode =
|
||||
| "NEXTAUTH_URL"
|
||||
| "NO_SECRET"
|
||||
| "TWITTER_OAUTH_2_BETA"
|
||||
| "DEBUG_ENABLED"
|
||||
|
||||
/**
|
||||
* Override any of the methods, and the rest will use the default logger.
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
"skipDefaultLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
"outDir": ".",
|
||||
|
||||
"paths": {
|
||||
"next": ["node_modules/next"]
|
||||
}
|
||||
},
|
||||
"exclude": ["./*.js", "./*.d.ts", "config", "**/__tests__", "tests"]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/tsconfig",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
@@ -7,4 +8,4 @@
|
||||
"adapters.json",
|
||||
"base.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
135
pnpm-lock.yaml
generated
135
pnpm-lock.yaml
generated
@@ -5,9 +5,8 @@ importers:
|
||||
.:
|
||||
specifiers:
|
||||
'@actions/core': ^1.6.0
|
||||
'@commitlint/parse': 16.0.0
|
||||
'@balazsorban/monorepo-release': 0.0.4
|
||||
'@types/node': ^17.0.25
|
||||
'@types/semver': 7.3.9
|
||||
'@typescript-eslint/eslint-plugin': ^5.10.2
|
||||
'@typescript-eslint/parser': ^4.33.0
|
||||
eslint: ^7.32.0
|
||||
@@ -17,21 +16,15 @@ importers:
|
||||
eslint-plugin-jest: ^25.3.0
|
||||
eslint-plugin-node: ^11.1.0
|
||||
eslint-plugin-promise: ^6.0.0
|
||||
git-log-parser: 1.2.0
|
||||
husky: ^7.0.4
|
||||
prettier: 2.4.1
|
||||
pretty-quick: ^3.1.2
|
||||
semver: 7.3.5
|
||||
stream-to-array: 2.3.0
|
||||
ts-node: 10.8.2
|
||||
turbo: 1.3.1
|
||||
type-fest: 2.16.0
|
||||
typescript: ^4.5.2
|
||||
devDependencies:
|
||||
'@actions/core': 1.9.0
|
||||
'@commitlint/parse': 16.0.0
|
||||
'@balazsorban/monorepo-release': 0.0.4
|
||||
'@types/node': 17.0.45
|
||||
'@types/semver': 7.3.9
|
||||
'@typescript-eslint/eslint-plugin': 5.29.0_3ekaj7j3owlolnuhj3ykrb7u7i
|
||||
'@typescript-eslint/parser': 4.33.0_hxadhbs2xogijvk7vq4t2azzbu
|
||||
eslint: 7.32.0
|
||||
@@ -41,15 +34,10 @@ importers:
|
||||
eslint-plugin-jest: 25.7.0_vibe533nrfhlkvcegtsn4treva
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 6.0.0_eslint@7.32.0
|
||||
git-log-parser: 1.2.0
|
||||
husky: 7.0.4
|
||||
prettier: 2.4.1
|
||||
pretty-quick: 3.1.3_prettier@2.4.1
|
||||
semver: 7.3.5
|
||||
stream-to-array: 2.3.0
|
||||
ts-node: 10.8.2_x2utdhayajzrh747hktprshhby
|
||||
turbo: 1.3.1
|
||||
type-fest: 2.16.0
|
||||
typescript: 4.7.4
|
||||
|
||||
apps/dev:
|
||||
@@ -389,6 +377,7 @@ importers:
|
||||
'@types/uuid': ^8.3.3
|
||||
'@upstash/redis': ^1.0.1
|
||||
dotenv: ^10.0.0
|
||||
isomorphic-fetch: 3.0.0
|
||||
jest: ^27.4.3
|
||||
next-auth: workspace:*
|
||||
uuid: ^8.3.2
|
||||
@@ -400,6 +389,7 @@ importers:
|
||||
'@types/uuid': 8.3.4
|
||||
'@upstash/redis': 1.7.0
|
||||
dotenv: 10.0.0
|
||||
isomorphic-fetch: 3.0.0
|
||||
jest: 27.5.1
|
||||
next-auth: link:../next-auth
|
||||
|
||||
@@ -3640,6 +3630,17 @@ packages:
|
||||
'@babel/helper-validator-identifier': 7.16.7
|
||||
to-fast-properties: 2.0.0
|
||||
|
||||
/@balazsorban/monorepo-release/0.0.4:
|
||||
resolution: {integrity: sha512-jjYc05vcRueT+nC7BD7C0D2JjE+H8xDdAIfwjtlbMHTnTwPx2KYXrbWohbL7bGVN8ZbhJDmXkXOQjppSrZCQBw==}
|
||||
engines: {node: '>=16.16.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@commitlint/parse': 17.0.0
|
||||
git-log-parser: 1.2.0
|
||||
semver: 7.3.7
|
||||
stream-to-array: 2.3.0
|
||||
dev: true
|
||||
|
||||
/@bcoe/v8-coverage/0.2.3:
|
||||
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
|
||||
dev: true
|
||||
@@ -3653,29 +3654,22 @@ packages:
|
||||
engines: {node: '>=0.1.90'}
|
||||
requiresBuild: true
|
||||
|
||||
/@commitlint/parse/16.0.0:
|
||||
resolution: {integrity: sha512-F9EjFlMw4MYgBEqoRrWZZKQBzdiJzPBI0qFDFqwUvfQsMmXEREZ242T4R5bFwLINWaALFLHEIa/FXEPa6QxCag==}
|
||||
engines: {node: '>=v12'}
|
||||
/@commitlint/parse/17.0.0:
|
||||
resolution: {integrity: sha512-cKcpfTIQYDG1ywTIr5AG0RAiLBr1gudqEsmAGCTtj8ffDChbBRxm6xXs2nv7GvmJN7msOt7vOKleLvcMmRa1+A==}
|
||||
engines: {node: '>=v14'}
|
||||
dependencies:
|
||||
'@commitlint/types': 16.2.1
|
||||
'@commitlint/types': 17.0.0
|
||||
conventional-changelog-angular: 5.0.13
|
||||
conventional-commits-parser: 3.2.4
|
||||
dev: true
|
||||
|
||||
/@commitlint/types/16.2.1:
|
||||
resolution: {integrity: sha512-7/z7pA7BM0i8XvMSBynO7xsB3mVQPUZbVn6zMIlp/a091XJ3qAXRXc+HwLYhiIdzzS5fuxxNIHZMGHVD4HJxdA==}
|
||||
engines: {node: '>=v12'}
|
||||
/@commitlint/types/17.0.0:
|
||||
resolution: {integrity: sha512-hBAw6U+SkAT5h47zDMeOu3HSiD0SODw4Aq7rRNh1ceUmL7GyLKYhPbUvlRWqZ65XjBLPHZhFyQlRaPNz8qvUyQ==}
|
||||
engines: {node: '>=v14'}
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
dev: true
|
||||
|
||||
/@cspotcode/source-map-support/0.8.1:
|
||||
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.9
|
||||
dev: true
|
||||
|
||||
/@dabh/diagnostics/2.0.3:
|
||||
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
|
||||
dependencies:
|
||||
@@ -5499,13 +5493,6 @@ packages:
|
||||
'@jridgewell/resolve-uri': 3.0.7
|
||||
'@jridgewell/sourcemap-codec': 1.4.13
|
||||
|
||||
/@jridgewell/trace-mapping/0.3.9:
|
||||
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
|
||||
dependencies:
|
||||
'@jridgewell/resolve-uri': 3.0.7
|
||||
'@jridgewell/sourcemap-codec': 1.4.13
|
||||
dev: true
|
||||
|
||||
/@js-joda/core/3.2.0:
|
||||
resolution: {integrity: sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg==}
|
||||
dev: true
|
||||
@@ -6442,22 +6429,6 @@ packages:
|
||||
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
/@tsconfig/node10/1.0.9:
|
||||
resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
|
||||
dev: true
|
||||
|
||||
/@tsconfig/node12/1.0.11:
|
||||
resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==}
|
||||
dev: true
|
||||
|
||||
/@tsconfig/node14/1.0.3:
|
||||
resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==}
|
||||
dev: true
|
||||
|
||||
/@tsconfig/node16/1.0.3:
|
||||
resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==}
|
||||
dev: true
|
||||
|
||||
/@types/aria-query/4.2.2:
|
||||
resolution: {integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==}
|
||||
dev: true
|
||||
@@ -6954,10 +6925,6 @@ packages:
|
||||
/@types/scheduler/0.16.2:
|
||||
resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
|
||||
|
||||
/@types/semver/7.3.9:
|
||||
resolution: {integrity: sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==}
|
||||
dev: true
|
||||
|
||||
/@types/serve-index/1.9.1:
|
||||
resolution: {integrity: sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==}
|
||||
dependencies:
|
||||
@@ -7764,10 +7731,6 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/arg/4.1.3:
|
||||
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
|
||||
dev: true
|
||||
|
||||
/arg/5.0.2:
|
||||
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
||||
dev: false
|
||||
@@ -9435,10 +9398,6 @@ packages:
|
||||
readable-stream: 3.6.0
|
||||
dev: true
|
||||
|
||||
/create-require/1.1.1:
|
||||
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
|
||||
dev: true
|
||||
|
||||
/cross-env/5.2.1:
|
||||
resolution: {integrity: sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ==}
|
||||
engines: {node: '>=4.0'}
|
||||
@@ -10568,11 +10527,6 @@ packages:
|
||||
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
|
||||
dev: true
|
||||
|
||||
/diff/4.0.2:
|
||||
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
dev: true
|
||||
|
||||
/dir-glob/3.0.1:
|
||||
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -20934,37 +20888,6 @@ packages:
|
||||
yargs-parser: 20.2.9
|
||||
dev: true
|
||||
|
||||
/ts-node/10.8.2_x2utdhayajzrh747hktprshhby:
|
||||
resolution: {integrity: sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@swc/core': '>=1.2.50'
|
||||
'@swc/wasm': '>=1.2.50'
|
||||
'@types/node': '*'
|
||||
typescript: '>=2.7'
|
||||
peerDependenciesMeta:
|
||||
'@swc/core':
|
||||
optional: true
|
||||
'@swc/wasm':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@cspotcode/source-map-support': 0.8.1
|
||||
'@tsconfig/node10': 1.0.9
|
||||
'@tsconfig/node12': 1.0.11
|
||||
'@tsconfig/node14': 1.0.3
|
||||
'@tsconfig/node16': 1.0.3
|
||||
'@types/node': 17.0.45
|
||||
acorn: 8.7.1
|
||||
acorn-walk: 8.2.0
|
||||
arg: 4.1.3
|
||||
create-require: 1.1.1
|
||||
diff: 4.0.2
|
||||
make-error: 1.3.6
|
||||
typescript: 4.7.4
|
||||
v8-compile-cache-lib: 3.0.1
|
||||
yn: 3.1.1
|
||||
dev: true
|
||||
|
||||
/tsconfig-paths/3.14.1:
|
||||
resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==}
|
||||
dependencies:
|
||||
@@ -21204,11 +21127,6 @@ packages:
|
||||
engines: {node: '>=12.20'}
|
||||
dev: false
|
||||
|
||||
/type-fest/2.16.0:
|
||||
resolution: {integrity: sha512-qpaThT2HQkFb83gMOrdKVsfCN7LKxP26Yq+smPzY1FqoHRjqmjqHXA7n5Gkxi8efirtbeEUxzfEdePthQWCuHw==}
|
||||
engines: {node: '>=12.20'}
|
||||
dev: true
|
||||
|
||||
/type-is/1.6.18:
|
||||
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -21759,10 +21677,6 @@ packages:
|
||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||
hasBin: true
|
||||
|
||||
/v8-compile-cache-lib/3.0.1:
|
||||
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
|
||||
dev: true
|
||||
|
||||
/v8-compile-cache/2.3.0:
|
||||
resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==}
|
||||
dev: true
|
||||
@@ -22422,11 +22336,6 @@ packages:
|
||||
yargs-parser: 21.0.1
|
||||
dev: true
|
||||
|
||||
/yn/3.1.1:
|
||||
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/yocto-queue/0.1.0:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
import type {
|
||||
Commit,
|
||||
GroupedCommits as GrouppedCommits,
|
||||
PackageToRelease,
|
||||
} from "./types"
|
||||
|
||||
import { debug, pkgJson, execSync } from "./utils"
|
||||
import semver from "semver"
|
||||
import parseCommit from "@commitlint/parse"
|
||||
import gitLog from "git-log-parser"
|
||||
import streamToArray from "stream-to-array"
|
||||
|
||||
export async function analyze(options: {
|
||||
dryRun: boolean
|
||||
packages: Record<string, string>
|
||||
BREAKING_COMMIT_MSG: string
|
||||
RELEASE_COMMIT_MSG: string
|
||||
RELEASE_COMMIT_TYPES: string[]
|
||||
}): Promise<PackageToRelease[]> {
|
||||
const {
|
||||
packages,
|
||||
BREAKING_COMMIT_MSG,
|
||||
RELEASE_COMMIT_MSG,
|
||||
RELEASE_COMMIT_TYPES,
|
||||
} = options
|
||||
|
||||
const packageFolders = Object.values(options.packages)
|
||||
|
||||
console.log("Identifying latest tag...")
|
||||
const latestTag = execSync("git describe --tags --abbrev=0", {
|
||||
stdio: "pipe",
|
||||
})
|
||||
.toString()
|
||||
.trim()
|
||||
console.log(`Latest tag identified: ${latestTag}`)
|
||||
|
||||
console.log()
|
||||
|
||||
console.log("Identifying commits since the latest tag...")
|
||||
|
||||
const range = `${latestTag}..HEAD`
|
||||
|
||||
// Get the commits since the latest tag
|
||||
const commitsSinceLatestTag = await new Promise<Commit[]>(
|
||||
(resolve, reject) => {
|
||||
const stream = gitLog.parse({ _: range })
|
||||
streamToArray(stream, (err: Error, arr: any[]) => {
|
||||
if (err) return reject(err)
|
||||
|
||||
Promise.all(
|
||||
arr.map(async (d) => {
|
||||
const parsed = await parseCommit(d.subject)
|
||||
|
||||
return { ...d, parsed }
|
||||
})
|
||||
).then((res) => resolve(res.filter(Boolean)))
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
console.log(commitsSinceLatestTag.length, `commits found since ${latestTag}`)
|
||||
debug(
|
||||
"Analyzing the following commits:",
|
||||
commitsSinceLatestTag.map((c) => ` ${c.subject}`).join("\n")
|
||||
)
|
||||
|
||||
const lastCommit = commitsSinceLatestTag[0]
|
||||
|
||||
if (lastCommit?.parsed.raw === RELEASE_COMMIT_MSG) {
|
||||
debug("Already released...")
|
||||
return []
|
||||
}
|
||||
|
||||
console.log()
|
||||
console.log("Identifying commits that touched package code...")
|
||||
function getChangedFiles(commitSha: string) {
|
||||
return execSync(
|
||||
`git diff-tree --no-commit-id --name-only -r ${commitSha}`,
|
||||
{ stdio: "pipe" }
|
||||
)
|
||||
.toString()
|
||||
.trim()
|
||||
.split("\n")
|
||||
}
|
||||
const packageCommits = commitsSinceLatestTag.filter(({ commit }) => {
|
||||
const changedFiles = getChangedFiles(commit.short)
|
||||
return packageFolders.some((packageFolder) =>
|
||||
changedFiles.some((changedFile) => changedFile.startsWith(packageFolder))
|
||||
)
|
||||
})
|
||||
|
||||
console.log(packageCommits.length, "commits touched package code")
|
||||
|
||||
console.log()
|
||||
|
||||
console.log("Identifying packages that need a new release...")
|
||||
|
||||
const packagesNeedRelease: string[] = []
|
||||
const grouppedPackages = packageCommits.reduce((acc, commit) => {
|
||||
const changedFilesInCommit = getChangedFiles(commit.commit.short)
|
||||
|
||||
for (const [pkg, src] of Object.entries(packages)) {
|
||||
if (
|
||||
changedFilesInCommit.some((changedFile) => changedFile.startsWith(src))
|
||||
) {
|
||||
if (!(pkg in acc)) {
|
||||
acc[pkg] = { features: [], bugfixes: [], other: [], breaking: [] }
|
||||
}
|
||||
const { type } = commit.parsed
|
||||
if (RELEASE_COMMIT_TYPES.includes(type)) {
|
||||
if (!packagesNeedRelease.includes(pkg)) {
|
||||
packagesNeedRelease.push(pkg)
|
||||
}
|
||||
if (type === "feat") {
|
||||
acc[pkg].features.push(commit)
|
||||
if (commit.body.includes(BREAKING_COMMIT_MSG)) {
|
||||
const [, changesBody] = commit.body.split(BREAKING_COMMIT_MSG)
|
||||
acc[pkg].breaking.push({
|
||||
...commit,
|
||||
body: changesBody.trim(),
|
||||
})
|
||||
}
|
||||
} else acc[pkg].bugfixes.push(commit)
|
||||
} else {
|
||||
acc[pkg].other.push(commit)
|
||||
}
|
||||
}
|
||||
}
|
||||
return acc
|
||||
}, {} as Record<string, GrouppedCommits>)
|
||||
|
||||
if (packagesNeedRelease.length) {
|
||||
console.log(
|
||||
packagesNeedRelease.length,
|
||||
`new release(s) needed: ${packagesNeedRelease.join(", ")}`
|
||||
)
|
||||
} else {
|
||||
console.log("No packages needed a new release, BYE!")
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
console.log()
|
||||
|
||||
const packagesToRelease: PackageToRelease[] = []
|
||||
for await (const pkgName of packagesNeedRelease) {
|
||||
const commits = grouppedPackages[pkgName]
|
||||
const releaseType: semver.ReleaseType = commits.breaking.length
|
||||
? "major" // 1.x.x
|
||||
: commits.features.length
|
||||
? "minor" // x.1.x
|
||||
: "patch" // x.x.1
|
||||
|
||||
const packageJson = await pkgJson.read(packages[pkgName])
|
||||
const oldVersion = packageJson.version!
|
||||
const newSemVer = semver.parse(semver.inc(oldVersion, releaseType))!
|
||||
|
||||
packagesToRelease.push({
|
||||
name: pkgName,
|
||||
oldVersion,
|
||||
newVersion: `${newSemVer.major}.${newSemVer.minor}.${newSemVer.patch}`,
|
||||
commits,
|
||||
path: packages[pkgName],
|
||||
})
|
||||
}
|
||||
|
||||
return packagesToRelease
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
export const config = {
|
||||
releaseBranches: ["main"],
|
||||
// TODO: Generate dynamically
|
||||
packages: {
|
||||
"next-auth": "packages/next-auth",
|
||||
"@next-auth/dgraph-adapter": "packages/adapter-dgraph",
|
||||
"@next-auth/fauna-adapter": "packages/adapter-fauna",
|
||||
"@next-auth/mikro-orm-adapter": "packages/adapter-mikro-orm",
|
||||
"@next-auth/neo4j-adapter": "packages/adapter-neo4j",
|
||||
"@next-auth/prisma-adapter": "packages/adapter-prisma",
|
||||
"@next-auth/upstash-redis-adapter": "packages/adapter-upstash-redis",
|
||||
"@next-auth/dynamodb-adapter": "packages/adapter-dynamodb",
|
||||
"@next-auth/firebase-adapter": "packages/adapter-firebase",
|
||||
"@next-auth/mongodb-adapter": "packages/adapter-mongodb",
|
||||
"@next-auth/pouchdb-adapter": "packages/adapter-pouchdb",
|
||||
"@next-auth/sequelize-adapter": "packages/adapter-sequelize",
|
||||
"@next-auth/typeorm-legacy-adapter": "packages/adapter-typeorm-legacy",
|
||||
},
|
||||
rootDir: process.cwd(),
|
||||
BREAKING_COMMIT_MSG: "BREAKING CHANGE:",
|
||||
RELEASE_COMMIT_MSG: "chore(release): bump package version(s) [skip ci]",
|
||||
RELEASE_COMMIT_TYPES: ["feat", "fix"],
|
||||
dryRun:
|
||||
!process.env.CI ||
|
||||
!!process.env.DRY_RUN ||
|
||||
process.argv.includes("--dry-run"),
|
||||
verbose: !!process.env.VERBOSE || process.argv.includes("--verbose"),
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { config } from "./config"
|
||||
import { shouldSkip } from "./skip"
|
||||
import { verify as verify } from "./verify"
|
||||
import { analyze } from "./analyze"
|
||||
import { publish } from "./publish"
|
||||
import { debug } from "./utils"
|
||||
|
||||
async function run() {
|
||||
if (config.dryRun) {
|
||||
console.log("\nPerforming dry run, no packages will be published!\n")
|
||||
}
|
||||
|
||||
if (shouldSkip({ releaseBranches: config.releaseBranches })) {
|
||||
return
|
||||
}
|
||||
|
||||
if (config.dryRun) {
|
||||
console.log("\nDry run, skip validation...\n")
|
||||
} else {
|
||||
await verify()
|
||||
}
|
||||
|
||||
const packages = await analyze(config)
|
||||
|
||||
if (!packages.length) return
|
||||
|
||||
debug(
|
||||
"Packages to release:",
|
||||
packages
|
||||
.map((p) =>
|
||||
JSON.stringify(
|
||||
{
|
||||
...p,
|
||||
commits: `${p.commits.features.length} feature(s), ${p.commits.bugfixes.length} bugfixe(s), ${p.commits.other.length} other(s) and ${p.commits.breaking.length} breaking change(s)`,
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
)
|
||||
.join("\n")
|
||||
)
|
||||
|
||||
await publish({ ...config, packages })
|
||||
}
|
||||
|
||||
run().catch((err) => {
|
||||
console.log(err)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -1,134 +0,0 @@
|
||||
import type { Commit, PackageToRelease } from "./types"
|
||||
|
||||
import { debug, pkgJson, execSync } from "./utils"
|
||||
|
||||
export async function publish(options: {
|
||||
dryRun: boolean
|
||||
packages: PackageToRelease[]
|
||||
RELEASE_COMMIT_MSG: string
|
||||
}) {
|
||||
const { dryRun, packages, RELEASE_COMMIT_MSG } = options
|
||||
|
||||
execSync("pnpm build")
|
||||
|
||||
for await (const pkg of packages) {
|
||||
if (dryRun) {
|
||||
console.log(
|
||||
`Dry run, \`npm publish\` would have released package \`${pkg.name}\` with version "${pkg.newVersion}".`
|
||||
)
|
||||
} else {
|
||||
console.log(
|
||||
`Writing version "${pkg.newVersion}" to package.json for package \`${pkg.name}\``
|
||||
)
|
||||
await pkgJson.update(pkg.path, { version: pkg.newVersion })
|
||||
console.log("package.json file has been written, publishing...")
|
||||
}
|
||||
|
||||
let npmPublish = `pnpm publish --access public --registry=https://registry.npmjs.org --no-git-checks`
|
||||
// We use different tokens for `next-auth` and `@next-auth/*` packages
|
||||
|
||||
if (pkg.name === "next-auth") {
|
||||
process.env.NPM_TOKEN = process.env.NPM_TOKEN_PKG
|
||||
} else {
|
||||
process.env.NPM_TOKEN = process.env.NPM_TOKEN_ORG
|
||||
}
|
||||
|
||||
if (dryRun) {
|
||||
console.log(
|
||||
`Dry run, skip \`npm publish\` for package \`${pkg.name}\`...\n`
|
||||
)
|
||||
npmPublish += " --dry-run --silent"
|
||||
} else {
|
||||
execSync(
|
||||
"echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc",
|
||||
{ cwd: pkg.path }
|
||||
)
|
||||
}
|
||||
|
||||
execSync(npmPublish, { cwd: pkg.path })
|
||||
}
|
||||
|
||||
if (dryRun) {
|
||||
console.log("Dry run, skip release commit...")
|
||||
} else {
|
||||
execSync(`git add -A && git commit -m "${RELEASE_COMMIT_MSG}"`)
|
||||
console.log("Commited.")
|
||||
}
|
||||
|
||||
for (const pkg of packages) {
|
||||
const { name, oldVersion, newVersion } = pkg
|
||||
const gitTag = `${name}@v${newVersion}`
|
||||
|
||||
console.log(
|
||||
`\n\n-------------------------------\n${name} ${oldVersion} -> ${newVersion}`
|
||||
)
|
||||
|
||||
const changelog = createChangelog(pkg)
|
||||
debug("Changelog generated", changelog)
|
||||
|
||||
if (dryRun) {
|
||||
console.log(`Dry run, skip git tag/release notes for package \`${name}\``)
|
||||
} else {
|
||||
console.log(`Creating git tag...`)
|
||||
execSync(`git tag ${gitTag}`)
|
||||
execSync("git push --tags")
|
||||
console.log(`Creating GitHub release notes...`)
|
||||
execSync(`gh release create ${gitTag} --notes '${changelog}'`)
|
||||
}
|
||||
}
|
||||
|
||||
if (!dryRun) {
|
||||
execSync(`git push`)
|
||||
}
|
||||
}
|
||||
|
||||
function createChangelog(pkg: PackageToRelease) {
|
||||
const {
|
||||
commits: { features, breaking, bugfixes, other },
|
||||
} = pkg
|
||||
console.log(`Creating changelog for package \`${pkg.name}\`...`)
|
||||
|
||||
let changelog = ``
|
||||
changelog += listGroup("Features", features)
|
||||
changelog += listGroup("Bugfixes", bugfixes)
|
||||
changelog += listGroup("Other", other)
|
||||
|
||||
if (breaking.length) {
|
||||
changelog += `
|
||||
## BREAKING CHANGES
|
||||
|
||||
${breaking.map((c) => ` - ${c.body}`).join("\n")}`
|
||||
}
|
||||
|
||||
return changelog
|
||||
}
|
||||
|
||||
function sortByScope(commits: Commit[]) {
|
||||
return commits.sort((a, b) => {
|
||||
if (a.parsed.scope && b.parsed.scope) {
|
||||
return a.parsed.scope.localeCompare(b.parsed.scope)
|
||||
} else if (a.parsed.scope) return -1
|
||||
else if (b.parsed.scope) return 1
|
||||
return a.body.localeCompare(b.body)
|
||||
})
|
||||
}
|
||||
|
||||
function header(c: Commit) {
|
||||
let h = c.parsed.subject
|
||||
if (c.parsed.scope) {
|
||||
h = `**${c.parsed.scope}**: ${h} (${c.commit.short})`
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
function listGroup(heading: string, commits: Commit[]) {
|
||||
if (!commits.length) return ""
|
||||
const list = sortByScope(commits)
|
||||
.map((c) => ` - ${header(c)}`)
|
||||
.join("\n")
|
||||
return `## ${heading}
|
||||
|
||||
${list}
|
||||
|
||||
`
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
export function shouldSkip(options: { releaseBranches: string[] }) {
|
||||
if (!process.env.CI) {
|
||||
return false
|
||||
}
|
||||
const { releaseBranches } = options
|
||||
|
||||
const branch = process.env.GITHUB_REF_NAME
|
||||
if (!branch || !releaseBranches.includes(branch)) {
|
||||
console.log(`\nSkipping release for branch "${branch}"`)
|
||||
console.log(
|
||||
`Releases are only triggered for the following branches: ${releaseBranches.join(
|
||||
", "
|
||||
)}\n`
|
||||
)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "NodeNext",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true
|
||||
},
|
||||
"ts-node": {
|
||||
"swc": true
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
export interface Commit {
|
||||
commit: CommitOrTree
|
||||
tree: CommitOrTree
|
||||
author: AuthorOrCommitter
|
||||
committer: AuthorOrCommitter
|
||||
subject: string
|
||||
body: string
|
||||
parsed: Parsed
|
||||
}
|
||||
|
||||
export interface CommitOrTree {
|
||||
long: string
|
||||
short: string
|
||||
}
|
||||
|
||||
export interface AuthorOrCommitter {
|
||||
name: string
|
||||
email: string
|
||||
date: string
|
||||
}
|
||||
|
||||
export interface Parsed {
|
||||
type: string
|
||||
scope?: string | null
|
||||
subject: string
|
||||
merge?: null
|
||||
header: string
|
||||
body?: null
|
||||
footer?: null
|
||||
notes?: null[] | null
|
||||
references?: null[] | null
|
||||
mentions?: null[] | null
|
||||
revert?: null
|
||||
raw: string
|
||||
}
|
||||
|
||||
export interface Package {
|
||||
name: string
|
||||
srcDir: string
|
||||
peerDependencies?: string[]
|
||||
}
|
||||
|
||||
export interface BranchConfig {
|
||||
prerelease: boolean
|
||||
ghRelease: boolean
|
||||
}
|
||||
|
||||
export interface PackageToRelease {
|
||||
name: string
|
||||
newVersion: string
|
||||
oldVersion: string
|
||||
commits: GroupedCommits
|
||||
path: string
|
||||
}
|
||||
|
||||
export interface GroupedCommits {
|
||||
features: Commit[]
|
||||
bugfixes: Commit[]
|
||||
other: Commit[]
|
||||
breaking: Commit[]
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import type { PackageJson } from "type-fest"
|
||||
|
||||
import fs from "node:fs/promises"
|
||||
import path from "node:path"
|
||||
import { execSync as nodeExecSync } from "node:child_process"
|
||||
import { config } from "./config"
|
||||
|
||||
async function read(directory: string): Promise<PackageJson> {
|
||||
const content = await fs.readFile(
|
||||
path.join(process.cwd(), directory, "package.json"),
|
||||
"utf8"
|
||||
)
|
||||
return JSON.parse(content)
|
||||
}
|
||||
|
||||
async function update(
|
||||
directory: string,
|
||||
data: Partial<PackageJson>
|
||||
): Promise<void> {
|
||||
const original = await pkgJson.read(directory)
|
||||
const content = JSON.stringify({ ...original, ...data }, null, 2)
|
||||
await fs.writeFile(
|
||||
path.join(process.cwd(), directory, "package.json"),
|
||||
content,
|
||||
"utf8"
|
||||
)
|
||||
}
|
||||
|
||||
export const pkgJson = { read, update }
|
||||
|
||||
export function debug(...args: any[]): void {
|
||||
if (!config.verbose) return
|
||||
const [first, ...rest] = args
|
||||
console.log(`\n[debug] ${first}\n`, ...rest, "\n")
|
||||
}
|
||||
|
||||
export function execSync(...args: Parameters<typeof nodeExecSync>) {
|
||||
return nodeExecSync(args[0], { stdio: "inherit", ...args[1] })
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
export async function verify() {
|
||||
if (!process.env.NPM_TOKEN_PKG) {
|
||||
throw new Error("NPM_TOKEN_PKG is not set")
|
||||
}
|
||||
if (!process.env.NPM_TOKEN_ORG) {
|
||||
throw new Error("NPM_TOKEN_ORG is not set")
|
||||
}
|
||||
if (!process.env.RELEASE_TOKEN) {
|
||||
throw new Error("RELEASE_TOKEN is not set")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user