From ffc927759e19b20842536b91b94153817670f267 Mon Sep 17 00:00:00 2001 From: Sebastian Hierholzer Date: Sun, 3 Dec 2023 21:01:28 +0100 Subject: [PATCH 1/7] feat: refactored login --- .../users/userId/updateUserById.ts | 215 ++-- lib/api/verifyUser.ts | 7 +- pages/api/v1/auth/[...nextauth].ts | 1042 ++++++++++++++++- pages/api/v1/logins/index.ts | 31 + pages/login.tsx | 137 ++- pages/register.tsx | 3 +- types/enviornment.d.ts | 421 ++++++- 7 files changed, 1637 insertions(+), 219 deletions(-) create mode 100644 pages/api/v1/logins/index.ts diff --git a/lib/api/controllers/users/userId/updateUserById.ts b/lib/api/controllers/users/userId/updateUserById.ts index 1d08203..1f10ba9 100644 --- a/lib/api/controllers/users/userId/updateUserById.ts +++ b/lib/api/controllers/users/userId/updateUserById.ts @@ -13,45 +13,88 @@ export default async function updateUserById( userId: number, data: AccountSettings ) { - if (emailEnabled && !data.email) - return { - response: "Email invalid.", - status: 400, - }; - else if (!data.username) - return { - response: "Username invalid.", - status: 400, - }; - if (data.newPassword && data.newPassword?.length < 8) - return { - response: "Password must be at least 8 characters.", - status: 400, - }; - - // Check email (if enabled) - const checkEmail = - /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; - if (emailEnabled && !checkEmail.test(data.email?.toLowerCase() || "")) - return { - response: "Please enter a valid email.", - status: 400, - }; - - const checkUsername = RegExp("^[a-z0-9_-]{3,31}$"); - - if (!checkUsername.test(data.username.toLowerCase())) - return { - response: - "Username has to be between 3-30 characters, no spaces and special characters are allowed.", - status: 400, - }; - - const userIsTaken = await prisma.user.findFirst({ + const ssoUser = await prisma.account.findFirst({ where: { - id: { not: userId }, - OR: emailEnabled - ? [ + userId: userId, + }, + }); + const user = await prisma.user.findUnique({ + where: { + id: userId, + }, + }); + if (ssoUser) { // deny changes to SSO-defined properties + if (data.email !== user?.email) { + return { + response: "SSO users cannot change their email.", + status: 400, + }; + } + if (data.newPassword) { + return { + response: "SSO Users cannot change their password.", + status: 400, + }; + } + if (data.name !== user?.name) { + return { + response: "SSO Users cannot change their name.", + status: 400, + }; + } + if (data.username !== user?.username) { + return { + response: "SSO Users cannot change their username.", + status: 400, + }; + } + if (data.image !== "") { + return { + response: "SSO Users cannot change their avatar.", + status: 400, + }; + } + + } else { // verify only for non-SSO users + // SSO users cannot change their email, password, name, username, or avatar + if (emailEnabled && !data.email) + return { + response: "Email invalid.", + status: 400, + }; + else if (!data.username) + return { + response: "Username invalid.", + status: 400, + }; + if (data.newPassword && data.newPassword?.length < 8) + return { + response: "Password must be at least 8 characters.", + status: 400, + }; + // Check email (if enabled) + const checkEmail = + /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; + if (emailEnabled && !checkEmail.test(data.email?.toLowerCase() || "")) + return { + response: "Please enter a valid email.", + status: 400, + }; + + const checkUsername = RegExp("^[a-z0-9_-]{3,31}$"); + + if (!checkUsername.test(data.username.toLowerCase())) + return { + response: + "Username has to be between 3-30 characters, no spaces and special characters are allowed.", + status: 400, + }; + + const userIsTaken = await prisma.user.findFirst({ + where: { + id: { not: userId }, + OR: emailEnabled + ? [ { username: data.username.toLowerCase(), }, @@ -59,66 +102,70 @@ export default async function updateUserById( email: data.email?.toLowerCase(), }, ] - : [ + : [ { username: data.username.toLowerCase(), }, ], - }, - }); + }, + }); + + if (userIsTaken) { + if (data.email?.toLowerCase().trim() === userIsTaken.email?.trim()) + return { + response: "Email is taken.", + status: 400, + }; + else if ( + data.username?.toLowerCase().trim() === userIsTaken.username?.trim() + ) + return { + response: "Username is taken.", + status: 400, + }; - if (userIsTaken) { - if (data.email?.toLowerCase().trim() === userIsTaken.email?.trim()) return { - response: "Email is taken.", - status: 400, - }; - else if ( - data.username?.toLowerCase().trim() === userIsTaken.username?.trim() - ) - return { - response: "Username is taken.", - status: 400, - }; - - return { - response: "Username/Email is taken.", - status: 400, - }; - } - - // Avatar Settings - - if (data.image?.startsWith("data:image/jpeg;base64")) { - if (data.image.length < 1572864) { - try { - const base64Data = data.image.replace(/^data:image\/jpeg;base64,/, ""); - - createFolder({ filePath: `uploads/avatar` }); - - await createFile({ - filePath: `uploads/avatar/${userId}.jpg`, - data: base64Data, - isBase64: true, - }); - } catch (err) { - console.log("Error saving image:", err); - } - } else { - console.log("A file larger than 1.5MB was uploaded."); - return { - response: "A file larger than 1.5MB was uploaded.", + response: "Username/Email is taken.", status: 400, }; } - } else if (data.image == "") { - removeFile({ filePath: `uploads/avatar/${userId}.jpg` }); + + // Avatar Settings + + if (data.image?.startsWith("data:image/jpeg;base64")) { + if (data.image.length < 1572864) { + try { + const base64Data = data.image.replace(/^data:image\/jpeg;base64,/, ""); + + createFolder({ filePath: `uploads/avatar` }); + + await createFile({ + filePath: `uploads/avatar/${userId}.jpg`, + data: base64Data, + isBase64: true, + }); + } catch (err) { + console.log("Error saving image:", err); + } + } else { + console.log("A file larger than 1.5MB was uploaded."); + return { + response: "A file larger than 1.5MB was uploaded.", + status: 400, + }; + } + } else if (data.image == "") { + removeFile({ filePath: `uploads/avatar/${userId}.jpg` }); + } } const previousEmail = ( await prisma.user.findUnique({ where: { id: userId } }) )?.email; + + + // Other settings const saltRounds = 10; @@ -130,7 +177,7 @@ export default async function updateUserById( }, data: { name: data.name, - username: data.username.toLowerCase().trim(), + username: data.username?.toLowerCase().trim(), email: data.email?.toLowerCase().trim(), isPrivate: data.isPrivate, image: data.image ? `uploads/avatar/${userId}.jpg` : "", diff --git a/lib/api/verifyUser.ts b/lib/api/verifyUser.ts index 943f68e..6f59e97 100644 --- a/lib/api/verifyUser.ts +++ b/lib/api/verifyUser.ts @@ -31,13 +31,18 @@ export default async function verifyUser({ subscriptions: true, }, }); + const ssoUser = await prisma.account.findFirst({ + where: { + userId: userId, + }, + }); if (!user) { res.status(404).json({ response: "User not found." }); return null; } - if (!user.username) { + if (!user.username && !ssoUser) { // SSO users don't need a username res.status(401).json({ response: "Username not found.", }); diff --git a/pages/api/v1/auth/[...nextauth].ts b/pages/api/v1/auth/[...nextauth].ts index 4b5dd59..b18a3fb 100644 --- a/pages/api/v1/auth/[...nextauth].ts +++ b/pages/api/v1/auth/[...nextauth].ts @@ -1,41 +1,98 @@ -import { prisma } from "@/lib/api/db"; +import {prisma} from "@/lib/api/db"; import NextAuth from "next-auth/next"; import CredentialsProvider from "next-auth/providers/credentials"; -import { AuthOptions } from "next-auth"; +import {AuthOptions} from "next-auth"; import bcrypt from "bcrypt"; import EmailProvider from "next-auth/providers/email"; -import { PrismaAdapter } from "@auth/prisma-adapter"; -import { Adapter } from "next-auth/adapters"; +import {PrismaAdapter} from "@auth/prisma-adapter"; +import {Adapter} from "next-auth/adapters"; import sendVerificationRequest from "@/lib/api/sendVerificationRequest"; -import { Provider } from "next-auth/providers"; +import {Provider} from "next-auth/providers"; import verifySubscription from "@/lib/api/verifySubscription"; +import FortyTwoProvider from "next-auth/providers/42-school"; +import AppleProvider from "next-auth/providers/apple"; +import AtlassianProvider from "next-auth/providers/atlassian"; +import Auth0Provider from "next-auth/providers/auth0"; +import AuthentikProvider from "next-auth/providers/authentik"; +import BattleNetProvider, {BattleNetIssuer} from "next-auth/providers/battlenet"; +import BoxProvider from "next-auth/providers/box"; +import CognitoProvider from "next-auth/providers/cognito"; +import CoinbaseProvider from "next-auth/providers/coinbase"; +import DiscordProvider from "next-auth/providers/discord"; +import DropboxProvider from "next-auth/providers/dropbox"; +import DuendeIDS6Provider from "next-auth/providers/duende-identity-server6"; +import EVEOnlineProvider from "next-auth/providers/eveonline"; +import FacebookProvider from "next-auth/providers/facebook"; +import FaceItProvider from "next-auth/providers/faceit"; +import FourSquareProvider from "next-auth/providers/foursquare"; +import FreshbooksProvider from "next-auth/providers/freshbooks"; +import FusionAuthProvider from "next-auth/providers/fusionauth"; +import GitHubProvider from "next-auth/providers/github"; +import GitlabProvider from "next-auth/providers/gitlab"; +import GoogleProvider from "next-auth/providers/google"; +import HubspotProvider from "next-auth/providers/hubspot"; +import IdentityServer4Provider from "next-auth/providers/identity-server4"; +import KakaoProvider from "next-auth/providers/kakao"; import KeycloakProvider from "next-auth/providers/keycloak"; +import LineProvider from "next-auth/providers/line"; +import LinkedInProvider from "next-auth/providers/linkedin"; +import MailchimpProvider from "next-auth/providers/mailchimp"; +import MailRuProvider from "next-auth/providers/mailru"; +import NaverProvider from "next-auth/providers/naver"; +import NetlifyProvider from "next-auth/providers/netlify"; +import OktaProvider from "next-auth/providers/okta"; +import OneLoginProvider from "next-auth/providers/onelogin"; +import OssoProvider from "next-auth/providers/osso"; +import OsuProvider from "next-auth/providers/osu"; +import PatreonProvider from "next-auth/providers/patreon"; +import PinterestProvider from "next-auth/providers/pinterest"; +import PipedriveProvider from "next-auth/providers/pipedrive"; +import RedditProvider from "next-auth/providers/reddit"; +import SalesforceProvider from "next-auth/providers/salesforce"; +import SlackProvider from "next-auth/providers/slack"; +import SpotifyProvider from "next-auth/providers/spotify"; +import StravaProvider from "next-auth/providers/strava"; +import TodoistProvider from "next-auth/providers/todoist"; +import TwitchProvider from "next-auth/providers/twitch"; +import UnitedEffectsProvider from "next-auth/providers/united-effects"; +import VkProvider from "next-auth/providers/vk"; +import WikimediaProvider from "next-auth/providers/wikimedia"; +import WordpressProvider from "next-auth/providers/wordpress"; +import YandexProvider from "next-auth/providers/yandex"; +import ZitadelProvider from "next-auth/providers/zitadel"; +import ZohoProvider from "next-auth/providers/zoho"; +import ZoomProvider from "next-auth/providers/zoom" +import * as process from "process"; const emailEnabled = process.env.EMAIL_FROM && process.env.EMAIL_SERVER ? true : false; -const keycloakEnabled = process.env.NEXT_PUBLIC_KEYCLOAK_ENABLED === "true"; +const newSsoUsersDisabled = process.env.DISABLE_NEW_SSO_USERS === 'true'; +; const adapter = PrismaAdapter(prisma); const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY; -const providers: Provider[] = [ - CredentialsProvider({ - type: "credentials", - credentials: {}, - async authorize(credentials, req) { - console.log("User log in attempt..."); - if (!credentials) return null; +const providers: Provider[] = []; - const { username, password } = credentials as { - username: string; - password: string; - }; +if (process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === 'true' || process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === undefined) { // undefined is for backwards compatibility + providers.push( + CredentialsProvider({ + type: "credentials", + credentials: {}, + async authorize(credentials, req) { + console.log("User log in attempt..."); + if (!credentials) return null; - const user = await prisma.user.findFirst({ - where: emailEnabled - ? { + const {username, password} = credentials as { + username: string; + password: string; + }; + + const user = await prisma.user.findFirst({ + where: emailEnabled + ? { OR: [ { username: username.toLowerCase(), @@ -44,25 +101,26 @@ const providers: Provider[] = [ email: username?.toLowerCase(), }, ], - emailVerified: { not: null }, + emailVerified: {not: null}, } - : { + : { username: username.toLowerCase(), }, - }); + }); - let passwordMatches: boolean = false; + let passwordMatches: boolean = false; - if (user?.password) { - passwordMatches = bcrypt.compareSync(password, user.password); - } + if (user?.password) { + passwordMatches = bcrypt.compareSync(password, user.password); + } - if (passwordMatches) { - return { id: user?.id }; - } else return null as any; - }, - }), -]; + if (passwordMatches) { + return {id: user?.id}; + } else return null as any; + }, + }) + ) +} if (emailEnabled) { providers.push( @@ -77,18 +135,456 @@ if (emailEnabled) { ); } -if (keycloakEnabled) { +// 42 School +if (process.env.NEXT_PUBLIC_FORTYTWO_ENABLED === 'true') { + providers.push( + FortyTwoProvider({ + id: "42-school", + name: "42 School", + clientId: process.env.FORTY_TWO_CLIENT_ID!, + clientSecret: process.env.FORTY_TWO_CLIENT_SECRET!, + profile(profile) { + return { + id: profile.id.toString(), + name: profile.usual_full_name, + email: profile.email, + image: profile.image_url, + username: profile.id.toString(), + } + }, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Apple +if (process.env.NEXT_PUBLIC_APPLE_ENABLED === 'true') { + providers.push( + AppleProvider({ + clientId: process.env.APPLE_CLIENT_ID!, + clientSecret: process.env.APPLE_CLIENT_SECRET!, + profile(profile) { + return { + id: profile.sub, + name: profile.name, + email: profile.email, + image: null, + username: profile.sub, + } + }, + + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Atlassian +if (process.env.NEXT_PUBLIC_ATLASSIAN_ENABLED === 'true') { + providers.push( + AtlassianProvider({ + clientId: process.env.ATLASSIAN_CLIENT_ID!, + clientSecret: process.env.ATLASSIAN_CLIENT_SECRET!, + authorization: { + params: { + scope: "write:jira-work read:jira-work read:jira-user offline_access read:me" + } + }, + profile(profile) { + return { + id: profile.account_id, + name: profile.name, + email: profile.email, + image: profile.picture, + username: profile.account_id, + } + }, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Auth0 +if(process.env.NEXT_PUBLIC_AUTH0_ENABLED === 'true') { + providers.push( + Auth0Provider({ + clientId: process.env.AUTH0_CLIENT_ID!, + clientSecret: process.env.AUTH0_CLIENT_SECRET!, + issuer: process.env.AUTH0_ISSUER + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Authentik +if (process.env.NEXT_PUBLIC_AUTHENTIK_ENABLED === 'true') { + providers.push( + AuthentikProvider({ + clientId: process.env.AUTHENTIK_CLIENT_ID!, + clientSecret: process.env.AUTHENTIK_CLIENT_SECRET!, + issuer: process.env.AUTHENTIK_ISSUER, + profile: (profile) => { + return { + id: profile.sub, + name: profile.name ?? profile.preferred_username, + email: profile.email, + image: profile.picture, + }; + }, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Battle.net +if(process.env.NEXT_PUBLIC_BATTLENET_ENABLED === 'true') { + providers.push( + BattleNetProvider({ + clientId: process.env.BATTLENET_CLIENT_ID!, + clientSecret: process.env.BATTLENET_CLIENT_SECRET!, + issuer: process.env.BATLLENET_ISSUER as BattleNetIssuer + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Box +if(process.env.NEXT_PUBLIC_BOX_ENABLED === 'true') { + providers.push( + BoxProvider({ + clientId: process.env.BOX_CLIENT_ID, + clientSecret: process.env.BOX_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Cognito +if(process.env.NEXT_PUBLIC_COGNITO_ENABLED === 'true') { + providers.push( + CognitoProvider({ + clientId: process.env.COGNITO_CLIENT_ID!, + clientSecret: process.env.COGNITO_CLIENT_SECRET!, + issuer: process.env.COGNITO_ISSUER, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Coinbase +if(process.env.NEXT_PUBLIC_COINBASE_ENABLED === 'true') { + providers.push( + CoinbaseProvider({ + clientId: process.env.COINBASE_CLIENT_ID, + clientSecret: process.env.COINBASE_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Discord +if(process.env.NEXT_PUBLIC_DISCORD_ENABLED === 'true') { + providers.push( + DiscordProvider({ + clientId: process.env.DISCORD_CLIENT_ID!, + clientSecret: process.env.DISCORD_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Dropbox +if(process.env.NEXT_PUBLIC_DROPBOX_ENABLED === 'true') { + providers.push( + DropboxProvider({ + clientId: process.env.DROPBOX_CLIENT_ID, + clientSecret: process.env.DROPBOX_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Duende IdentityServer6 +if(process.env.NEXT_PUBLIC_DUENDE_IDS6_ENABLED === 'true') { + providers.push( + DuendeIDS6Provider({ + clientId: process.env.DUENDE_IDS6_ID!, + clientSecret: process.env.DUENDE_IDS6_SECRET!, + issuer: process.env.DUENDE_IDS6_ISSUER, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// EVE Online +if(process.env.NEXT_PUBLIC_EVEONLINE_ENABLED === 'true') { + providers.push( + EVEOnlineProvider({ + clientId: process.env.EVE_CLIENT_ID!, + clientSecret: process.env.EVE_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Facebook +if(process.env.NEXT_PUBLIC_FACEBOOK_ENABLED === 'true') { + providers.push( + FacebookProvider({ + clientId: process.env.FACEBOOK_CLIENT_ID!, + clientSecret: process.env.FACEBOOK_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// FACEIT +if(process.env.NEXT_PUBLIC_FACEIT_ENABLED === 'true') { + providers.push( + FaceItProvider({ + clientId: process.env.FACEIT_CLIENT_ID, + clientSecret: process.env.FACEIT_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Foursquare +if(process.env.NEXT_PUBLIC_FOURSQUARE_ENABLED === 'true') { + providers.push( + FourSquareProvider({ + clientId: process.env.FOURSQUARE_CLIENT_ID, + clientSecret: process.env.FOURSQUARE_CLIENT_SECRET, + apiVersion: process.env.FOURSQUARE_APIVERSION + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Freshbooks +if(process.env.NEXT_PUBLIC_FRESHBOOKS_ENABLED === 'true') { + providers.push( + FreshbooksProvider({ + clientId: process.env.FRESHBOOKS_CLIENT_ID, + clientSecret: process.env.FRESHBOOKS_CLIENT_SECRET, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// FusionAuth +if(process.env.NEXT_PUBLIC_FUSIONAUTH_ENABLED === 'true') { + providers.push( + FusionAuthProvider({ + id: "fusionauth", + name: "FusionAuth", + issuer: process.env.FUSIONAUTH_ISSUER, + clientId: process.env.FUSIONAUTH_CLIENT_ID!, + clientSecret: process.env.FUSIONAUTH_SECRET!, + tenantId: process.env.FUSIONAUTH_TENANT_ID + }), + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// GitHub +if(process.env.NEXT_PUBLIC_GITHUB_ENABLED === 'true') { + providers.push( + GitHubProvider({ + clientId: process.env.GITHUB_ID!, + clientSecret: process.env.GITHUB_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// GitLab +if(process.env.NEXT_PUBLIC_GITLAB_ENABLED === 'true') { + providers.push( + GitlabProvider({ + clientId: process.env.GITLAB_CLIENT_ID!, + clientSecret: process.env.GITLAB_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Google +if(process.env.NEXT_PUBLIC_GOOGLE_ENABLED === 'true') { + providers.push( + GoogleProvider({ + clientId: process.env.GOOGLE_CLIENT_ID!, + clientSecret: process.env.GOOGLE_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// HubSpot +if(process.env.NEXT_PUBLIC_HUBSPOT_ENABLED === 'true') { + providers.push( + HubspotProvider({ + clientId: process.env.HUBSPOT_CLIENT_ID!, + clientSecret: process.env.HUBSPOT_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// IdentityServer4 +if(process.env.NEXT_PUBLIC_IDS4_ENABLED === 'true') { + providers.push( + IdentityServer4Provider({ + id: "identity-server4", + name: "IdentityServer4", + issuer: process.env.IdentityServer4_Issuer, + clientId: process.env.IdentityServer4_CLIENT_ID, + clientSecret: process.env.IdentityServer4_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Kakao +if(process.env.NEXT_PUBLIC_KAKAO_ENABLED === 'true') { + providers.push( + KakaoProvider({ + clientId: process.env.KAKAO_CLIENT_ID!, + clientSecret: process.env.KAKAO_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Keycloak +if (process.env.NEXT_PUBLIC_KEYCLOAK_ENABLED === 'true') { providers.push( KeycloakProvider({ - id: "keycloak", - name: "Keycloak", clientId: process.env.KEYCLOAK_CLIENT_ID!, clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!, issuer: process.env.KEYCLOAK_ISSUER, profile: (profile) => { return { id: profile.sub, - username: profile.preferred_username, name: profile.name ?? profile.preferred_username, email: profile.email, image: profile.picture, @@ -98,11 +594,464 @@ if (keycloakEnabled) { ); const _linkAccount = adapter.linkAccount; adapter.linkAccount = (account) => { - const { "not-before-policy": _, refresh_expires_in, ...data } = account; + const {"not-before-policy": _, refresh_expires_in, ...data} = account; return _linkAccount ? _linkAccount(data) : undefined; }; } +// LINE +if(process.env.NEXT_PUBLIC_LINE_ENABLED === 'true') { + providers.push( + LineProvider({ + clientId: process.env.LINE_CLIENT_ID!, + clientSecret: process.env.LINE_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// LinkedIn +if(process.env.NEXT_PUBLIC_LINKEDIN_ENABLED === 'true') { + providers.push( + LinkedInProvider({ + clientId: process.env.LINKEDIN_CLIENT_ID!, + clientSecret: process.env.LINKEDIN_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Mailchimp +if(process.env.NEXT_PUBLIC_MAILCHIMP_ENABLED === 'true') { + providers.push( + MailchimpProvider({ + clientId: process.env.MAILCHIMP_CLIENT_ID, + clientSecret: process.env.MAILCHIMP_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Mail.ru +if(process.env.NEXT_PUBLIC_MAILRU_ENABLED === 'true') { + providers.push( + MailRuProvider({ + clientId: process.env.MAILRU_CLIENT_ID, + clientSecret: process.env.MAILRU_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Naver +if(process.env.NEXT_PUBLIC_NAVER_ENABLED === 'true') { + providers.push( + NaverProvider({ + clientId: process.env.NAVER_CLIENT_ID!, + clientSecret: process.env.NAVER_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Netlify +if(process.env.NEXT_PUBLIC_NETLIFY_ENABLED === 'true') { + providers.push( + NetlifyProvider({ + clientId: process.env.NETLIFY_CLIENT_ID, + clientSecret: process.env.NETLIFY_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Okta +if(process.env.NEXT_PUBLIC_OKTA_ENABLED === 'true') { + providers.push( + OktaProvider({ + clientId: process.env.OKTA_CLIENT_ID!, + clientSecret: process.env.OKTA_CLIENT_SECRET!, + issuer: process.env.OKTA_ISSUER + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// OneLogin +if(process.env.NEXT_PUBLIC_ONELOGIN_ENABLED === 'true') { + providers.push( + OneLoginProvider({ + clientId: process.env.ONELOGIN_CLIENT_ID, + clientSecret: process.env.ONELOGIN_CLIENT_SECRET, + issuer: process.env.ONELOGIN_ISSUER + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Osso +if(process.env.NEXT_PUBLIC_OSSO_ENABLED === 'true') { + providers.push( + OssoProvider({ + clientId: process.env.OSSO_CLIENT_ID, + clientSecret: process.env.OSSO_CLIENT_SECRET, + issuer: process.env.OSSO_ISSUER + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// osu! +if(process.env.NEXT_PUBLIC_OSU_ENABLED === 'true') { + providers.push( + OsuProvider({ + clientId: process.env.OSU_CLIENT_ID!, + clientSecret: process.env.OSU_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Patreon +if(process.env.NEXT_PUBLIC_PATREON_ENABLED === 'true') { + providers.push( + PatreonProvider({ + clientId: process.env.PATREON_ID!, + clientSecret: process.env.PATREON_SECRET!, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Pinterest +if(process.env.NEXT_PUBLIC_PINTEREST_ENABLED === 'true') { + providers.push( + PinterestProvider({ + clientId: process.env.PINTEREST_ID!, + clientSecret: process.env.PINTEREST_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Pipedrive +if(process.env.NEXT_PUBLIC_PIPEDRIVE_ENABLED === 'true') { + providers.push( + PipedriveProvider({ + clientId: process.env.PIPEDRIVE_CLIENT_ID!, + clientSecret: process.env.PIPEDRIVE_CLIENT_SECRET!, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Reddit +if(process.env.NEXT_PUBLIC_REDDIT_ENABLED === 'true') { + providers.push( + RedditProvider({ + clientId: process.env.REDDIT_CLIENT_ID, + clientSecret: process.env.REDDIT_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Salesforce +if(process.env.NEXT_PUBLIC_SALESFORCE_ENABLED === 'true') { + providers.push( + SalesforceProvider({ + clientId: process.env.SALESFORCE_CLIENT_ID!, + clientSecret: process.env.SALESFORCE_CLIENT_SECRET!, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Slack +if(process.env.NEXT_PUBLIC_SLACK_ENABLED === 'true') { + providers.push( + SlackProvider({ + clientId: process.env.SLACK_CLIENT_ID!, + clientSecret: process.env.SLACK_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Spotify +if(process.env.NEXT_PUBLIC_SPOTIFY_ENABLED === 'true') { + providers.push( + SpotifyProvider({ + clientId: process.env.SPOTIFY_CLIENT_ID!, + clientSecret: process.env.SPOTIFY_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Strava +if(process.env.NEXT_PUBLIC_STRAVA_ENABLED === 'true') { + providers.push( + StravaProvider({ + clientId: process.env.STRAVA_CLIENT_ID!, + clientSecret: process.env.STRAVA_CLIENT_SECRET!, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Todoist +if(process.env.NEXT_PUBLIC_TODOIST_ENABLED === 'true') { + providers.push( + TodoistProvider({ + clientId: process.env.TODOIST_ID!, + clientSecret: process.env.TODOIST_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Twitch +if(process.env.NEXT_PUBLIC_TWITCH_ENABLED === 'true') { + providers.push( + TwitchProvider({ + clientId: process.env.TWITCH_CLIENT_ID!, + clientSecret: process.env.TWITCH_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// United Effects +if(process.env.NEXT_PUBLIC_UNITED_EFFECTS_ENABLED === 'true') { + providers.push( + UnitedEffectsProvider({ + clientId: process.env.UNITED_EFFECTS_CLIENT_ID!, + clientSecret: process.env.UNITED_EFFECTS_CLIENT_SECRET!, + issuer: process.env.UNITED_EFFECTS_ISSUER! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// VK +if(process.env.NEXT_PUBLIC_VK_ENABLED === 'true') { + providers.push( + VkProvider({ + clientId: process.env.VK_CLIENT_ID!, + clientSecret: process.env.VK_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Wikimedia +if(process.env.NEXT_PUBLIC_WIKIMEDIA_ENABLED === 'true') { + providers.push( + WikimediaProvider({ + clientId: process.env.WIKIMEDIA_CLIENT_ID!, + clientSecret: process.env.WIKIMEDIA_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Wordpress.com +if(process.env.NEXT_PUBLIC_WORDPRESS_ENABLED === 'true') { + providers.push( + WordpressProvider({ + clientId: process.env.WORDPRESS_CLIENT_ID, + clientSecret: process.env.WORDPRESS_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Yandex +if(process.env.NEXT_PUBLIC_YANDEX_ENABLED === 'true') { + providers.push( + YandexProvider({ + clientId: process.env.YANDEX_CLIENT_ID!, + clientSecret: process.env.YANDEX_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Zitadel +if(process.env.NEXT_PUBLIC_ZITADEL_ENABLED === 'true') { + providers.push( + ZitadelProvider({ + issuer: process.env.ZITADEL_ISSUER, + clientId: process.env.ZITADEL_CLIENT_ID!, + clientSecret: process.env.ZITADEL_CLIENT_SECRET!, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Zoho +if(process.env.NEXT_PUBLIC_ZOHO_ENABLED === 'true') { + providers.push( + ZohoProvider({ + clientId: process.env.ZOHO_CLIENT_ID, + clientSecret: process.env.ZOHO_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Zoom +if(process.env.NEXT_PUBLIC_ZOOM_ENABLED_ENABLED === 'true') { + providers.push( + ZoomProvider({ + clientId: process.env.ZOOM_CLIENT_ID!, + clientSecret: process.env.ZOOM_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + export const authOptions: AuthOptions = { adapter: adapter as Adapter, session: { @@ -115,14 +1064,27 @@ export const authOptions: AuthOptions = { verifyRequest: "/confirmation", }, callbacks: { - async jwt({ token, trigger, user }) { + async signIn({user, account, profile, email, credentials}) { + if (account?.provider !== "credentials") { // registration via SSO can be separately disabled + const existingUser = await prisma.account.findFirst({ + where: { + providerAccountId: account?.providerAccountId, + }, + }); + if (existingUser && newSsoUsersDisabled) { + return false; + } + } + return true; + }, + async jwt({token, trigger, user}) { token.sub = token.sub ? Number(token.sub) : undefined; if (trigger === "signIn" || trigger === "signUp") token.id = user?.id as number; return token; }, - async session({ session, token }) { + async session({session, token}) { session.user.id = token.id; if (STRIPE_SECRET_KEY) { diff --git a/pages/api/v1/logins/index.ts b/pages/api/v1/logins/index.ts new file mode 100644 index 0000000..04a7fdd --- /dev/null +++ b/pages/api/v1/logins/index.ts @@ -0,0 +1,31 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import * as process from "process"; + +export type ResponseData = { + credentialsEnabled: string|undefined + emailEnabled: string|undefined + registrationDisabled: string|undefined + buttonAuths: { + method: string + name: string + }[] +} +export default function handler(req: NextApiRequest, res: NextApiResponse) { + res.json(getLogins()); +} + +export function getLogins() { + const buttonAuths = [] + if (process.env.NEXT_PUBLIC_KEYCLOAK_ENABLED === 'true') { + buttonAuths.push({method: 'keycloak', name: 'Keycloak'}); + } + if (process.env.NEXT_PUBLIC_AUTHENTIK_ENABLED === 'true') { + buttonAuths.push({method: 'authentik', name: process.env.AUTHENTIK_CUSTOM_NAME ?? 'Authentik'}); + } + return { + credentialsEnabled: (process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === 'true' || process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === undefined) ? "true" : "false", + emailEnabled: process.env.NEXT_PUBLIC_EMAIL_PROVIDER, + registrationDisabled: process.env.NEXT_PUBLIC_DISABLE_REGISTRATION, + buttonAuths: buttonAuths + }; +} \ No newline at end of file diff --git a/pages/login.tsx b/pages/login.tsx index b1a7ce4..3cee3de 100644 --- a/pages/login.tsx +++ b/pages/login.tsx @@ -5,16 +5,21 @@ import { signIn } from "next-auth/react"; import Link from "next/link"; import { useState, FormEvent } from "react"; import { toast } from "react-hot-toast"; +import {getLogins} from './api/v1/logins' +import {InferGetServerSidePropsType} from "next"; interface FormData { username: string; password: string; } -const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER; -const keycloakEnabled = process.env.NEXT_PUBLIC_KEYCLOAK_ENABLED; +export const getServerSideProps = (() => { + const availableLogins = getLogins(); + return {props: {availableLogins}} +}); -export default function Login() { + +export default function Login({availableLogins} : InferGetServerSidePropsType) { const [submitLoader, setSubmitLoader] = useState(false); const [form, setForm] = useState({ @@ -48,96 +53,106 @@ export default function Login() { } } - async function loginUserKeycloak() { + async function loginUserButton(method: string) { setSubmitLoader(true); const load = toast.loading("Authenticating..."); - const res = await signIn("keycloak", {}); + const res = await signIn(method, {}); toast.dismiss(load); setSubmitLoader(false); } - return ( - -
-
-

- Enter your credentials + function displayLoginCredential() { + if (availableLogins.credentialsEnabled === 'true') { + return (<>

+ Enter your credentials +

+
+
+

+ Username + {availableLogins.emailEnabled === 'true' ? " or Email" : undefined}

-
- -
-

- Username - {emailEnabled ? " or Email" : undefined} -

- - setForm({ ...form, username: e.target.value })} - /> -
+ onChange={(e) => setForm({...form, username: e.target.value})}/> +
+
+

+ Password +

-
-

- Password -

- - setForm({ ...form, password: e.target.value })} - /> - {emailEnabled && ( + onChange={(e) => setForm({...form, password: e.target.value})}/> + {availableLogins.emailEnabled === 'true' && (
Forgot Password?
- )} -
- - + - {process.env.NEXT_PUBLIC_KEYCLOAK_ENABLED === "true" ? ( - - ) : undefined} - {process.env.NEXT_PUBLIC_DISABLE_REGISTRATION === - "true" ? undefined : ( -
-

- New here? -

- + ) + } + } + function displayLoginExternalButton() { + const Buttons: any = []; + availableLogins.buttonAuths.forEach((value, index) => { + Buttons.push( loginUserButton(value.method)} + label={`Sign in with ${value.name}`} + className=" w-full text-center" + loading={submitLoader} + />); + }); + return (Buttons); + } + + function displayRegistration() { + if (availableLogins.registrationDisabled !== 'true') { + return ( +
+

+ New here? +

+ - Sign Up - -
- )} + > + Sign Up + +
+ ); + } + } + + return ( + + +
+ {displayLoginCredential()} + {displayLoginExternalButton()} + {displayRegistration()}
diff --git a/pages/register.tsx b/pages/register.tsx index f836843..a110fb4 100644 --- a/pages/register.tsx +++ b/pages/register.tsx @@ -1,13 +1,12 @@ import Link from "next/link"; import { useState, FormEvent } from "react"; import { toast } from "react-hot-toast"; -import SubmitButton from "@/components/SubmitButton"; import { signIn } from "next-auth/react"; import { useRouter } from "next/router"; import CenteredForm from "@/layouts/CenteredForm"; import TextInput from "@/components/TextInput"; -const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER; +const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER === "true"; type FormData = { name: string; diff --git a/types/enviornment.d.ts b/types/enviornment.d.ts index d2bdbb8..fa11a94 100644 --- a/types/enviornment.d.ts +++ b/types/enviornment.d.ts @@ -1,40 +1,399 @@ declare global { - namespace NodeJS { - interface ProcessEnv { - NEXTAUTH_SECRET: string; - DATABASE_URL: string; - NEXTAUTH_URL: string; - NEXT_PUBLIC_DISABLE_REGISTRATION?: string; - PAGINATION_TAKE_COUNT?: string; - STORAGE_FOLDER?: string; - AUTOSCROLL_TIMEOUT?: string; - RE_ARCHIVE_LIMIT?: string; + namespace NodeJS { + interface ProcessEnv { + NEXTAUTH_SECRET: string; + DATABASE_URL: string; + NEXTAUTH_URL: string; + NEXT_PUBLIC_DISABLE_REGISTRATION?: string; + PAGINATION_TAKE_COUNT?: string; + STORAGE_FOLDER?: string; + AUTOSCROLL_TIMEOUT?: string; + RE_ARCHIVE_LIMIT?: string; - SPACES_KEY?: string; - SPACES_SECRET?: string; - SPACES_ENDPOINT?: string; - SPACES_BUCKET_NAME?: string; - SPACES_REGION?: string; - SPACES_FORCE_PATH_STYLE?: string; + SPACES_KEY?: string; + SPACES_SECRET?: string; + SPACES_ENDPOINT?: string; + SPACES_BUCKET_NAME?: string; + SPACES_REGION?: string; + SPACES_FORCE_PATH_STYLE?: string; - NEXT_PUBLIC_KEYCLOAK_ENABLED?: string; - KEYCLOAK_ISSUER?: string; - KEYCLOAK_CLIENT_ID?: string; - KEYCLOAK_CLIENT_SECRET?: string; + NEXT_PUBLIC_CREDENTIALS_ENABLED?: string; + DISABLE_NEW_SSO_USERS?: string; - NEXT_PUBLIC_EMAIL_PROVIDER?: string; - EMAIL_FROM?: string; - EMAIL_SERVER?: string; + NEXT_PUBLIC_EMAIL_PROVIDER?: string; + EMAIL_FROM?: string; + EMAIL_SERVER?: string; - NEXT_PUBLIC_STRIPE?: string; - STRIPE_SECRET_KEY?: string; - MONTHLY_PRICE_ID?: string; - YEARLY_PRICE_ID?: string; - NEXT_PUBLIC_STRIPE_BILLING_PORTAL_URL?: string; - NEXT_PUBLIC_TRIAL_PERIOD_DAYS?: string; - BASE_URL?: string; + NEXT_PUBLIC_STRIPE?: string; + STRIPE_SECRET_KEY?: string; + MONTHLY_PRICE_ID?: string; + YEARLY_PRICE_ID?: string; + NEXT_PUBLIC_STRIPE_BILLING_PORTAL_URL?: string; + NEXT_PUBLIC_TRIAL_PERIOD_DAYS?: string; + BASE_URL?: string; + + // + // SSO Providers + // + + // 42 School + NEXT_PUBLIC_FORTYTWO_ENABLED?: string; + FORTYTWO_CUSTOM_NAME?: string; + FORTYTWO_CLIENT_ID?: string; + FORTYTWO_CLIENT_SECRET?: string; + + // Apple + NEXT_PUBLIC_APPLE_ENABLED?: string; + APPLE_CUSTOM_NAME?: string; + APPLE_ID?: string; + APPLE_SECRET?: string; + + // Atlassian + NEXT_PUBLIC_ATLASSIAN_ENABLED?: string; + ATLASSIAN_CUSTOM_NAME?: string; + ATLASSIAN_CLIENT_ID?: string; + ATLASSIAN_CLIENT_SECRET?: string; + ATLASSIAN_SCOPE?: string; + + // Auth0 + NEXT_PUBLIC_AUTH0_ENABLED?: string; + AUTH0_CUSTOM_NAME?: string; + AUTH0_ISSUER?: string; + AUTH0_CLIENT_SECRET?: string; + AUTH0_CLIENT_ID?: string; + + // Authentik + NEXT_PUBLIC_AUTHENTIK_ENABLED?: string; + AUTHENTIK_CUSTOM_NAME?: string; + AUTHENTIK_ISSUER?: string; + AUTHENTIK_CLIENT_ID?: string; + AUTHENTIK_CLIENT_SECRET?: string; + + // TODO: Azure AD B2C + // TODO: Azure AD + + // Battle.net + NEXT_PUBLIC_BATTLENET_ENABLED?: string; + BATTLENET_CUSTOM_NAME?: string; + BATTLENET_CLIENT_ID?: string; + BATTLENET_CLIENT_SECRET?: string; + BATLLENET_ISSUER?: string; + + // Box + NEXT_PUBLIC_BOX_ENABLED?: string; + BOX_CUSTOM_NAME?: string; + BOX_CLIENT_ID?: string; + BOX_CLIENT_SECRET?: string; + + // TODO: BoxyHQ SAML + + // Bungie + NEXT_PUBLIC_BUNGIE_ENABLED?: string; + BUNGIE_CUSTOM_NAME?: string; + BUNGIE_CLIENT_ID?: string; + BUNGIE_CLIENT_SECRET?: string; + BUNGIE_API_KEY?: string; + + // Cognito + NEXT_PUBLIC_COGNITO_ENABLED?: string; + COGNITO_CUSTOM_NAME?: string; + COGNITO_CLIENT_ID?: string; + COGNITO_CLIENT_SECRET?: string; + COGNITO_ISSUER?: string; + + // Coinbase + NEXT_PUBLIC_COINBASE_ENABLED?: string; + COINBASE_CUSTOM_NAME?: string; + COINBASE_CLIENT_ID?: string; + COINBASE_CLIENT_SECRET?: string; + + // Discord + NEXT_PUBLIC_DISCORD_ENABLED?: string; + DISCORD_CUSTOM_NAME?: string; + DISCORD_CLIENT_ID?: string; + DISCORD_CLIENT_SECRET?: string; + + // Dropbox + NEXT_PUBLIC_DROPBOX_ENABLED?: string; + DROPBOX_CUSTOM_NAME?: string; + DROPBOX_CLIENT_ID?: string; + DROPBOX_CLIENT_SECRET?: string; + + // DuendeIndentityServer6 + NEXT_PUBLIC_DUENDE_IDS6_ENABLED?: string; + DUENDE_IDS6_CUSTOM_NAME?: string; + DUENDE_IDS6_CLIENT_ID?: string; + DUENDE_IDS6_CLIENT_SECRET?: string; + DUENDE_IDS6_ISSUER?: string; + + // EVE Online + NEXT_PUBLIC_EVEONLINE_ENABLED?: string; + EVEONLINE_CUSTOM_NAME?: string; + EVEONLINE_CLIENT_ID?: string; + EVEONLINE_CLIENT_SECRET?: string; + + // Facebook + NEXT_PUBLIC_FACEBOOK_ENABLED?: string; + FACEBOOK_CUSTOM_NAME?: string; + FACEBOOK_CLIENT_ID?: string; + FACEBOOK_CLIENT_SECRET?: string; + + // FACEIT + NEXT_PUBLIC_FACEIT_ENABLED?: string; + FACEIT_CUSTOM_NAME?: string; + FACEIT_CLIENT_ID?: string; + FACEIT_CLIENT_SECRET?: string; + + // Foursquare + NEXT_PUBLIC_FOURSQUARE_ENABLED?: string; + FOURSQUARE_CUSTOM_NAME?: string; + FOURSQUARE_CLIENT_ID?: string; + FOURSQUARE_CLIENT_SECRET?: string; + FOURSQUARE_APIVERSION?: string; + + // Freshbooks + NEXT_PUBLIC_FRESHBOOKS_ENABLED?: string; + FRESHBOOKS_CUSTOM_NAME?: string; + FRESHBOOKS_CLIENT_ID?: string; + FRESHBOOKS_CLIENT_SECRET?: string; + + // FusionAuth + NEXT_PUBLIC_FUSIONAUTH_ENABLED?: string; + FUSIONAUTH_CUSTOM_NAME?: string; + FUSIONAUTH_CLIENT_ID?: string; + FUSIONAUTH_CLIENT_SECRET?: string; + FUSIONAUTH_ISSUER?: string; + FUSIONAUTH_TENANT_ID?: string; + + // GitHub + NEXT_PUBLIC_GITHUB_ENABLED?: string; + GITHUB_CUSTOM_NAME?: string; + GITHUB_CLIENT_ID?: string; + GITHUB_CLIENT_SECRET?: string; + + // GitLab + NEXT_PUBLIC_GITLAB_ENABLED?: string; + GITLAB_CUSTOM_NAME?: string; + GITLAB_CLIENT_ID?: string; + GITLAB_CLIENT_SECRET?: string; + + // Google + NEXT_PUBLIC_GOOGLE_ENABLED?: string; + NEXT_PUBLIC_GOOGLE_CUSTOM_NAME?: string; + GOOGLE_CLIENT_ID?: string; + GOOGLE_CLIENT_SECRET?: string; + + // HubSpot + NEXT_PUBLIC_HUBSPOT_ENABLED?: string; + HUBSPOT_CUSTOM_NAME?: string; + HUBSPOT_CLIENT_ID?: string; + HUBSPOT_CLIENT_SECRET?: string; + + // IdentityServer4 + NEXT_PUBLIC_IDS4_ENABLED?: string; + IDS4_CUSTOM_NAME?: string; + IDS4_CLIENT_ID?: string; + IDS4_CLIENT_SECRET?: string; + IDS4_ISSUER?: string; + + // TODO: Instagram (Doesn't return email) + + // Kakao + NEXT_PUBLIC_KAKAO_ENABLED?: string; + KAOKAO_CUSTOM_NAME?: string; + KAKAO_CLIENT_ID?: string; + KAKAO_CLIENT_SECRET?: string; + + // Keycloak + NEXT_PUBLIC_KEYCLOAK_ENABLED?: string; + KEYCLOAK_CUSTOM_NAME?: string; + KEYCLOAK_ISSUER?: string; + KEYCLOAK_CLIENT_ID?: string; + KEYCLOAK_CLIENT_SECRET?: string; + + // LINE + NEXT_PUBLIC_LINE_ENABLED?: string; + LINE_CUSTOM_NAME?: string; + LINE_CLIENT_ID?: string; + LINE_CLIENT_SECRET?: string; + + // LinkedIn + NEXT_PUBLIC_LINKEDIN_ENABLED?: string; + LINKEDIN_CUSTOM_NAME?: string; + LINKEDIN_CLIENT_ID?: string; + LINKEDIN_CLIENT_SECRET?: string; + + // Mailchimp + NEXT_PUBLIC_MAILCHIMP_ENABLED?: string; + MAILCHIMP_CUSTOM_NAME?: string; + MAILCHIMP_CLIENT_ID?: string; + MAILCHIMP_CLIENT_SECRET?: string; + + // Mail.ru + NEXT_PUBLIC_MAILRU_ENABLED?: string; + MAILRU_CUSTOM_NAME?: string; + MAILRU_CLIENT_ID?: string; + MAILRU_CLIENT_SECRET?: string; + + // TODO: Medium (Doesn't return email) + + // Naver + NEXT_PUBLIC_NAVER_ENABLED?: string; + NAVER_CUSTOM_NAME?: string; + NAVER_CLIENT_ID?: string; + NAVER_CLIENT_SECRET?: string; + + // Netlify + NEXT_PUBLIC_NETLIFY_ENABLED?: string; + NETLIFY_CUSTOM_NAME?: string; + NETLIFY_CLIENT_ID?: string; + NETLIFY_CLIENT_SECRET?: string; + + // Okta + NEXT_PUBLIC_OKTA_ENABLED?: string; + OKTA_CUSTOM_NAME?: string; + OKTA_CLIENT_ID?: string; + OKTA_CLIENT_SECRET?: string; + OKTA_ISSUER?: string; + + // OneLogin + NEXT_PUBLIC_ONELOGIN_ENABLED?: string; + ONELOGIN_CUSTOM_NAME?: string; + ONELOGIN_CLIENT_ID?: string; + ONELOGIN_CLIENT_SECRET?: string; + ONELOGIN_ISSUER?: string; + + // Osso + NEXT_PUBLIC_OSSO_ENABLED?: string; + OSSO_CUSTOM_NAME?: string; + OSSO_CLIENT_ID?: string; + OSSO_CLIENT_SECRET?: string; + OSSO_ISSUER?: string; + + // osu! + NEXT_PUBLIC_OSU_ENABLED?: string; + OSU_CUSTOM_NAME?: string; + OSU_CLIENT_ID?: string; + OSU_CLIENT_SECRET?: string; + + // Patreon + NEXT_PUBLIC_PATREON_ENABLED?: string; + PATREON_CUSTOM_NAME?: string; + PATREON_CLIENT_ID?: string; + PATREON_CLIENT_SECRET?: string; + + // Pinterest + NEXT_PUBLIC_PINTEREST_ENABLED?: string; + PINTEREST_CUSTOM_NAME?: string; + PINTEREST_CLIENT_ID?: string; + PINTEREST_CLIENT_SECRET?: string; + + // Pipedrive + NEXT_PUBLIC_PIPEDRIVE_ENABLED?: string; + PIPEDRIVE_CUSTOM_NAME?: string; + PIPEDRIVE_CLIENT_ID?: string; + PIPEDRIVE_CLIENT_SECRET?: string; + + // Reddit + // TODO (1h tokens) + NEXT_PUBLIC_REDDIT_ENABLED?: string; + REDDIT_CUSTOM_NAME?: string; + REDDIT_CLIENT_ID?: string; + REDDIT_CLIENT_SECRET?: string; + + // Salesforce + NEXT_PUBLIC_SALESFORCE_ENABLED?: string; + SALESFORCE_CUSTOM_NAME?: string; + SALESFORCE_CLIENT_ID?: string; + SALESFORCE_CLIENT_SECRET?: string; + + // Slack + NEXT_PUBLIC_SLACK_ENABLED?: string; + SLACK_CUSTOM_NAME?: string; + SLACK_CLIENT_ID?: string; + SLACK_CLIENT_SECRET?: string; + + // Spotify + NEXT_PUBLIC_SPOTIFY_ENABLED?: string; + SPOTIFY_CUSTOM_NAME?: string; + SPOTIFY_CLIENT_ID?: string; + SPOTIFY_CLIENT_SECRET?: string; + + // Strava + NEXT_PUBLIC_STRAVA_ENABLED?: string; + STRAVA_CUSTOM_NAME?: string; + STRAVA_CLIENT_ID?: string; + STRAVA_CLIENT_SECRET?: string; + + // Todoist + NEXT_PUBLIC_TODOIST_ENABLED?: string; + TODOIST_CUSTOM_NAME?: string; + TODOIST_CLIENT_ID?: string; + TODOIST_CLIENT_SECRET?: string; + + // TODO: Trakt (Doesn't return email) + + // Twitch + NEXT_PUBLIC_TWITCH_ENABLED?: string; + TWITCH_CUSTOM_NAME?: string; + TWITCH_CLIENT_ID?: string; + TWITCH_CLIENT_SECRET?: string; + + // TODO: Twitter (OAuth 1.0) + + // United Effects + NEXT_PUBLIC_UNITED_EFFECTS_ENABLED?: string; + UNITED_EFFECTS_CUSTOM_NAME?: string; + UNITED_EFFECTS_CLIENT_ID?: string; + UNITED_EFFECTS_CLIENT_SECRET?: string; + UNITED_EFFECTS_ISSUER?: string; + + // VK + NEXT_PUBLIC_VK_ENABLED?: string; + VK_CUSTOM_NAME?: string; + VK_CLIENT_ID?: string; + VK_CLIENT_SECRET?: string; + + // Wikimedia + NEXT_PUBLIC_WIKIMEDIA_ENABLED?: string; + WIKIMEDIA_CUSTOM_NAME?: string; + WIKIMEDIA_CLIENT_ID?: string; + WIKIMEDIA_CLIENT_SECRET?: string; + + // Wordpress.com + NEXT_PUBLIC_WORDPRESS_ENABLED?: string; + WORDPRESS_CUSTOM_NAME?: string; + WORDPRESS_CLIENT_ID?: string; + WORDPRESS_CLIENT_SECRET?: string; + + // TODO: WorkOS (Custom flow) + + // Yandex + NEXT_PUBLIC_YANDEX_ENABLED?: string; + YANDEX_CUSTOM_NAME?: string; + YANDEX_CLIENT_ID?: string; + YANDEX_CLIENT_SECRET?: string; + + // Zitadel + NEXT_PUBLIC_ZITADEL_ENABLED?: string; + ZITADEL_CUSTOM_NAME?: string; + ZITADEL_CLIENT_ID?: string; + ZITADEL_CLIENT_SECRET?: string; + ZITADEL_ISSUER?: string; + + // Zoho + NEXT_PUBLIC_ZOHO_ENABLED?: string; + ZOHO_CUSTOM_NAME?: string; + ZOHO_CLIENT_ID?: string; + ZOHO_CLIENT_SECRET?: string; + + // Zoom + NEXT_PUBLIC_ZOOM_ENABLED?: string; + ZOOM_CUSTOM_NAME?: string; + ZOOM_CLIENT_ID?: string; + ZOOM_CLIENT_SECRET?: string; + } } - } } export {}; From ba72de19ef6ee500e9a501f9b4bbb064d2fc6bdc Mon Sep 17 00:00:00 2001 From: Sebastian Hierholzer Date: Sun, 3 Dec 2023 22:19:02 +0100 Subject: [PATCH 2/7] fix: add getLogins boilerplate --- pages/api/v1/logins/index.ts | 211 ++++++++++++++++++++++++++++++++++- types/enviornment.d.ts | 2 +- 2 files changed, 210 insertions(+), 3 deletions(-) diff --git a/pages/api/v1/logins/index.ts b/pages/api/v1/logins/index.ts index 04a7fdd..c592451 100644 --- a/pages/api/v1/logins/index.ts +++ b/pages/api/v1/logins/index.ts @@ -16,12 +16,219 @@ export default function handler(req: NextApiRequest, res: NextApiResponse Date: Sun, 3 Dec 2023 21:01:28 +0100 Subject: [PATCH 3/7] feat: refactored login --- .../users/userId/updateUserById.ts | 215 ++-- lib/api/verifyUser.ts | 7 +- pages/api/v1/auth/[...nextauth].ts | 1056 ++++++++++++++++- pages/api/v1/logins/index.ts | 31 + pages/login.tsx | 188 ++- pages/register.tsx | 3 +- types/enviornment.d.ts | 384 +++++- 7 files changed, 1624 insertions(+), 260 deletions(-) create mode 100644 pages/api/v1/logins/index.ts diff --git a/lib/api/controllers/users/userId/updateUserById.ts b/lib/api/controllers/users/userId/updateUserById.ts index 1d08203..1f10ba9 100644 --- a/lib/api/controllers/users/userId/updateUserById.ts +++ b/lib/api/controllers/users/userId/updateUserById.ts @@ -13,45 +13,88 @@ export default async function updateUserById( userId: number, data: AccountSettings ) { - if (emailEnabled && !data.email) - return { - response: "Email invalid.", - status: 400, - }; - else if (!data.username) - return { - response: "Username invalid.", - status: 400, - }; - if (data.newPassword && data.newPassword?.length < 8) - return { - response: "Password must be at least 8 characters.", - status: 400, - }; - - // Check email (if enabled) - const checkEmail = - /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; - if (emailEnabled && !checkEmail.test(data.email?.toLowerCase() || "")) - return { - response: "Please enter a valid email.", - status: 400, - }; - - const checkUsername = RegExp("^[a-z0-9_-]{3,31}$"); - - if (!checkUsername.test(data.username.toLowerCase())) - return { - response: - "Username has to be between 3-30 characters, no spaces and special characters are allowed.", - status: 400, - }; - - const userIsTaken = await prisma.user.findFirst({ + const ssoUser = await prisma.account.findFirst({ where: { - id: { not: userId }, - OR: emailEnabled - ? [ + userId: userId, + }, + }); + const user = await prisma.user.findUnique({ + where: { + id: userId, + }, + }); + if (ssoUser) { // deny changes to SSO-defined properties + if (data.email !== user?.email) { + return { + response: "SSO users cannot change their email.", + status: 400, + }; + } + if (data.newPassword) { + return { + response: "SSO Users cannot change their password.", + status: 400, + }; + } + if (data.name !== user?.name) { + return { + response: "SSO Users cannot change their name.", + status: 400, + }; + } + if (data.username !== user?.username) { + return { + response: "SSO Users cannot change their username.", + status: 400, + }; + } + if (data.image !== "") { + return { + response: "SSO Users cannot change their avatar.", + status: 400, + }; + } + + } else { // verify only for non-SSO users + // SSO users cannot change their email, password, name, username, or avatar + if (emailEnabled && !data.email) + return { + response: "Email invalid.", + status: 400, + }; + else if (!data.username) + return { + response: "Username invalid.", + status: 400, + }; + if (data.newPassword && data.newPassword?.length < 8) + return { + response: "Password must be at least 8 characters.", + status: 400, + }; + // Check email (if enabled) + const checkEmail = + /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; + if (emailEnabled && !checkEmail.test(data.email?.toLowerCase() || "")) + return { + response: "Please enter a valid email.", + status: 400, + }; + + const checkUsername = RegExp("^[a-z0-9_-]{3,31}$"); + + if (!checkUsername.test(data.username.toLowerCase())) + return { + response: + "Username has to be between 3-30 characters, no spaces and special characters are allowed.", + status: 400, + }; + + const userIsTaken = await prisma.user.findFirst({ + where: { + id: { not: userId }, + OR: emailEnabled + ? [ { username: data.username.toLowerCase(), }, @@ -59,66 +102,70 @@ export default async function updateUserById( email: data.email?.toLowerCase(), }, ] - : [ + : [ { username: data.username.toLowerCase(), }, ], - }, - }); + }, + }); + + if (userIsTaken) { + if (data.email?.toLowerCase().trim() === userIsTaken.email?.trim()) + return { + response: "Email is taken.", + status: 400, + }; + else if ( + data.username?.toLowerCase().trim() === userIsTaken.username?.trim() + ) + return { + response: "Username is taken.", + status: 400, + }; - if (userIsTaken) { - if (data.email?.toLowerCase().trim() === userIsTaken.email?.trim()) return { - response: "Email is taken.", - status: 400, - }; - else if ( - data.username?.toLowerCase().trim() === userIsTaken.username?.trim() - ) - return { - response: "Username is taken.", - status: 400, - }; - - return { - response: "Username/Email is taken.", - status: 400, - }; - } - - // Avatar Settings - - if (data.image?.startsWith("data:image/jpeg;base64")) { - if (data.image.length < 1572864) { - try { - const base64Data = data.image.replace(/^data:image\/jpeg;base64,/, ""); - - createFolder({ filePath: `uploads/avatar` }); - - await createFile({ - filePath: `uploads/avatar/${userId}.jpg`, - data: base64Data, - isBase64: true, - }); - } catch (err) { - console.log("Error saving image:", err); - } - } else { - console.log("A file larger than 1.5MB was uploaded."); - return { - response: "A file larger than 1.5MB was uploaded.", + response: "Username/Email is taken.", status: 400, }; } - } else if (data.image == "") { - removeFile({ filePath: `uploads/avatar/${userId}.jpg` }); + + // Avatar Settings + + if (data.image?.startsWith("data:image/jpeg;base64")) { + if (data.image.length < 1572864) { + try { + const base64Data = data.image.replace(/^data:image\/jpeg;base64,/, ""); + + createFolder({ filePath: `uploads/avatar` }); + + await createFile({ + filePath: `uploads/avatar/${userId}.jpg`, + data: base64Data, + isBase64: true, + }); + } catch (err) { + console.log("Error saving image:", err); + } + } else { + console.log("A file larger than 1.5MB was uploaded."); + return { + response: "A file larger than 1.5MB was uploaded.", + status: 400, + }; + } + } else if (data.image == "") { + removeFile({ filePath: `uploads/avatar/${userId}.jpg` }); + } } const previousEmail = ( await prisma.user.findUnique({ where: { id: userId } }) )?.email; + + + // Other settings const saltRounds = 10; @@ -130,7 +177,7 @@ export default async function updateUserById( }, data: { name: data.name, - username: data.username.toLowerCase().trim(), + username: data.username?.toLowerCase().trim(), email: data.email?.toLowerCase().trim(), isPrivate: data.isPrivate, image: data.image ? `uploads/avatar/${userId}.jpg` : "", diff --git a/lib/api/verifyUser.ts b/lib/api/verifyUser.ts index 943f68e..6f59e97 100644 --- a/lib/api/verifyUser.ts +++ b/lib/api/verifyUser.ts @@ -31,13 +31,18 @@ export default async function verifyUser({ subscriptions: true, }, }); + const ssoUser = await prisma.account.findFirst({ + where: { + userId: userId, + }, + }); if (!user) { res.status(404).json({ response: "User not found." }); return null; } - if (!user.username) { + if (!user.username && !ssoUser) { // SSO users don't need a username res.status(401).json({ response: "Username not found.", }); diff --git a/pages/api/v1/auth/[...nextauth].ts b/pages/api/v1/auth/[...nextauth].ts index bb4f3ae..b18a3fb 100644 --- a/pages/api/v1/auth/[...nextauth].ts +++ b/pages/api/v1/auth/[...nextauth].ts @@ -1,43 +1,98 @@ -import { prisma } from "@/lib/api/db"; +import {prisma} from "@/lib/api/db"; import NextAuth from "next-auth/next"; import CredentialsProvider from "next-auth/providers/credentials"; -import { AuthOptions } from "next-auth"; +import {AuthOptions} from "next-auth"; import bcrypt from "bcrypt"; import EmailProvider from "next-auth/providers/email"; -import { PrismaAdapter } from "@auth/prisma-adapter"; -import { Adapter } from "next-auth/adapters"; +import {PrismaAdapter} from "@auth/prisma-adapter"; +import {Adapter} from "next-auth/adapters"; import sendVerificationRequest from "@/lib/api/sendVerificationRequest"; -import { Provider } from "next-auth/providers"; +import {Provider} from "next-auth/providers"; import verifySubscription from "@/lib/api/verifySubscription"; -import KeycloakProvider from "next-auth/providers/keycloak"; +import FortyTwoProvider from "next-auth/providers/42-school"; +import AppleProvider from "next-auth/providers/apple"; +import AtlassianProvider from "next-auth/providers/atlassian"; +import Auth0Provider from "next-auth/providers/auth0"; import AuthentikProvider from "next-auth/providers/authentik"; +import BattleNetProvider, {BattleNetIssuer} from "next-auth/providers/battlenet"; +import BoxProvider from "next-auth/providers/box"; +import CognitoProvider from "next-auth/providers/cognito"; +import CoinbaseProvider from "next-auth/providers/coinbase"; +import DiscordProvider from "next-auth/providers/discord"; +import DropboxProvider from "next-auth/providers/dropbox"; +import DuendeIDS6Provider from "next-auth/providers/duende-identity-server6"; +import EVEOnlineProvider from "next-auth/providers/eveonline"; +import FacebookProvider from "next-auth/providers/facebook"; +import FaceItProvider from "next-auth/providers/faceit"; +import FourSquareProvider from "next-auth/providers/foursquare"; +import FreshbooksProvider from "next-auth/providers/freshbooks"; +import FusionAuthProvider from "next-auth/providers/fusionauth"; +import GitHubProvider from "next-auth/providers/github"; +import GitlabProvider from "next-auth/providers/gitlab"; +import GoogleProvider from "next-auth/providers/google"; +import HubspotProvider from "next-auth/providers/hubspot"; +import IdentityServer4Provider from "next-auth/providers/identity-server4"; +import KakaoProvider from "next-auth/providers/kakao"; +import KeycloakProvider from "next-auth/providers/keycloak"; +import LineProvider from "next-auth/providers/line"; +import LinkedInProvider from "next-auth/providers/linkedin"; +import MailchimpProvider from "next-auth/providers/mailchimp"; +import MailRuProvider from "next-auth/providers/mailru"; +import NaverProvider from "next-auth/providers/naver"; +import NetlifyProvider from "next-auth/providers/netlify"; +import OktaProvider from "next-auth/providers/okta"; +import OneLoginProvider from "next-auth/providers/onelogin"; +import OssoProvider from "next-auth/providers/osso"; +import OsuProvider from "next-auth/providers/osu"; +import PatreonProvider from "next-auth/providers/patreon"; +import PinterestProvider from "next-auth/providers/pinterest"; +import PipedriveProvider from "next-auth/providers/pipedrive"; +import RedditProvider from "next-auth/providers/reddit"; +import SalesforceProvider from "next-auth/providers/salesforce"; +import SlackProvider from "next-auth/providers/slack"; +import SpotifyProvider from "next-auth/providers/spotify"; +import StravaProvider from "next-auth/providers/strava"; +import TodoistProvider from "next-auth/providers/todoist"; +import TwitchProvider from "next-auth/providers/twitch"; +import UnitedEffectsProvider from "next-auth/providers/united-effects"; +import VkProvider from "next-auth/providers/vk"; +import WikimediaProvider from "next-auth/providers/wikimedia"; +import WordpressProvider from "next-auth/providers/wordpress"; +import YandexProvider from "next-auth/providers/yandex"; +import ZitadelProvider from "next-auth/providers/zitadel"; +import ZohoProvider from "next-auth/providers/zoho"; +import ZoomProvider from "next-auth/providers/zoom" +import * as process from "process"; const emailEnabled = process.env.EMAIL_FROM && process.env.EMAIL_SERVER ? true : false; -const keycloakEnabled = process.env.NEXT_PUBLIC_KEYCLOAK_ENABLED === "true"; -const authentikEnabled = process.env.NEXT_PUBLIC_AUTHENTIK_ENABLED === "true"; +const newSsoUsersDisabled = process.env.DISABLE_NEW_SSO_USERS === 'true'; +; const adapter = PrismaAdapter(prisma); const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY; -const providers: Provider[] = [ - CredentialsProvider({ - type: "credentials", - credentials: {}, - async authorize(credentials, req) { - console.log("User log in attempt..."); - if (!credentials) return null; +const providers: Provider[] = []; - const { username, password } = credentials as { - username: string; - password: string; - }; +if (process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === 'true' || process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === undefined) { // undefined is for backwards compatibility + providers.push( + CredentialsProvider({ + type: "credentials", + credentials: {}, + async authorize(credentials, req) { + console.log("User log in attempt..."); + if (!credentials) return null; - const user = await prisma.user.findFirst({ - where: emailEnabled - ? { + const {username, password} = credentials as { + username: string; + password: string; + }; + + const user = await prisma.user.findFirst({ + where: emailEnabled + ? { OR: [ { username: username.toLowerCase(), @@ -46,25 +101,26 @@ const providers: Provider[] = [ email: username?.toLowerCase(), }, ], - emailVerified: { not: null }, + emailVerified: {not: null}, } - : { + : { username: username.toLowerCase(), }, - }); + }); - let passwordMatches: boolean = false; + let passwordMatches: boolean = false; - if (user?.password) { - passwordMatches = bcrypt.compareSync(password, user.password); - } + if (user?.password) { + passwordMatches = bcrypt.compareSync(password, user.password); + } - if (passwordMatches) { - return { id: user?.id }; - } else return null as any; - }, - }), -]; + if (passwordMatches) { + return {id: user?.id}; + } else return null as any; + }, + }) + ) +} if (emailEnabled) { providers.push( @@ -79,18 +135,456 @@ if (emailEnabled) { ); } -if (keycloakEnabled) { +// 42 School +if (process.env.NEXT_PUBLIC_FORTYTWO_ENABLED === 'true') { + providers.push( + FortyTwoProvider({ + id: "42-school", + name: "42 School", + clientId: process.env.FORTY_TWO_CLIENT_ID!, + clientSecret: process.env.FORTY_TWO_CLIENT_SECRET!, + profile(profile) { + return { + id: profile.id.toString(), + name: profile.usual_full_name, + email: profile.email, + image: profile.image_url, + username: profile.id.toString(), + } + }, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Apple +if (process.env.NEXT_PUBLIC_APPLE_ENABLED === 'true') { + providers.push( + AppleProvider({ + clientId: process.env.APPLE_CLIENT_ID!, + clientSecret: process.env.APPLE_CLIENT_SECRET!, + profile(profile) { + return { + id: profile.sub, + name: profile.name, + email: profile.email, + image: null, + username: profile.sub, + } + }, + + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Atlassian +if (process.env.NEXT_PUBLIC_ATLASSIAN_ENABLED === 'true') { + providers.push( + AtlassianProvider({ + clientId: process.env.ATLASSIAN_CLIENT_ID!, + clientSecret: process.env.ATLASSIAN_CLIENT_SECRET!, + authorization: { + params: { + scope: "write:jira-work read:jira-work read:jira-user offline_access read:me" + } + }, + profile(profile) { + return { + id: profile.account_id, + name: profile.name, + email: profile.email, + image: profile.picture, + username: profile.account_id, + } + }, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Auth0 +if(process.env.NEXT_PUBLIC_AUTH0_ENABLED === 'true') { + providers.push( + Auth0Provider({ + clientId: process.env.AUTH0_CLIENT_ID!, + clientSecret: process.env.AUTH0_CLIENT_SECRET!, + issuer: process.env.AUTH0_ISSUER + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Authentik +if (process.env.NEXT_PUBLIC_AUTHENTIK_ENABLED === 'true') { + providers.push( + AuthentikProvider({ + clientId: process.env.AUTHENTIK_CLIENT_ID!, + clientSecret: process.env.AUTHENTIK_CLIENT_SECRET!, + issuer: process.env.AUTHENTIK_ISSUER, + profile: (profile) => { + return { + id: profile.sub, + name: profile.name ?? profile.preferred_username, + email: profile.email, + image: profile.picture, + }; + }, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Battle.net +if(process.env.NEXT_PUBLIC_BATTLENET_ENABLED === 'true') { + providers.push( + BattleNetProvider({ + clientId: process.env.BATTLENET_CLIENT_ID!, + clientSecret: process.env.BATTLENET_CLIENT_SECRET!, + issuer: process.env.BATLLENET_ISSUER as BattleNetIssuer + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Box +if(process.env.NEXT_PUBLIC_BOX_ENABLED === 'true') { + providers.push( + BoxProvider({ + clientId: process.env.BOX_CLIENT_ID, + clientSecret: process.env.BOX_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Cognito +if(process.env.NEXT_PUBLIC_COGNITO_ENABLED === 'true') { + providers.push( + CognitoProvider({ + clientId: process.env.COGNITO_CLIENT_ID!, + clientSecret: process.env.COGNITO_CLIENT_SECRET!, + issuer: process.env.COGNITO_ISSUER, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Coinbase +if(process.env.NEXT_PUBLIC_COINBASE_ENABLED === 'true') { + providers.push( + CoinbaseProvider({ + clientId: process.env.COINBASE_CLIENT_ID, + clientSecret: process.env.COINBASE_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Discord +if(process.env.NEXT_PUBLIC_DISCORD_ENABLED === 'true') { + providers.push( + DiscordProvider({ + clientId: process.env.DISCORD_CLIENT_ID!, + clientSecret: process.env.DISCORD_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Dropbox +if(process.env.NEXT_PUBLIC_DROPBOX_ENABLED === 'true') { + providers.push( + DropboxProvider({ + clientId: process.env.DROPBOX_CLIENT_ID, + clientSecret: process.env.DROPBOX_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Duende IdentityServer6 +if(process.env.NEXT_PUBLIC_DUENDE_IDS6_ENABLED === 'true') { + providers.push( + DuendeIDS6Provider({ + clientId: process.env.DUENDE_IDS6_ID!, + clientSecret: process.env.DUENDE_IDS6_SECRET!, + issuer: process.env.DUENDE_IDS6_ISSUER, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// EVE Online +if(process.env.NEXT_PUBLIC_EVEONLINE_ENABLED === 'true') { + providers.push( + EVEOnlineProvider({ + clientId: process.env.EVE_CLIENT_ID!, + clientSecret: process.env.EVE_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Facebook +if(process.env.NEXT_PUBLIC_FACEBOOK_ENABLED === 'true') { + providers.push( + FacebookProvider({ + clientId: process.env.FACEBOOK_CLIENT_ID!, + clientSecret: process.env.FACEBOOK_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// FACEIT +if(process.env.NEXT_PUBLIC_FACEIT_ENABLED === 'true') { + providers.push( + FaceItProvider({ + clientId: process.env.FACEIT_CLIENT_ID, + clientSecret: process.env.FACEIT_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Foursquare +if(process.env.NEXT_PUBLIC_FOURSQUARE_ENABLED === 'true') { + providers.push( + FourSquareProvider({ + clientId: process.env.FOURSQUARE_CLIENT_ID, + clientSecret: process.env.FOURSQUARE_CLIENT_SECRET, + apiVersion: process.env.FOURSQUARE_APIVERSION + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Freshbooks +if(process.env.NEXT_PUBLIC_FRESHBOOKS_ENABLED === 'true') { + providers.push( + FreshbooksProvider({ + clientId: process.env.FRESHBOOKS_CLIENT_ID, + clientSecret: process.env.FRESHBOOKS_CLIENT_SECRET, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// FusionAuth +if(process.env.NEXT_PUBLIC_FUSIONAUTH_ENABLED === 'true') { + providers.push( + FusionAuthProvider({ + id: "fusionauth", + name: "FusionAuth", + issuer: process.env.FUSIONAUTH_ISSUER, + clientId: process.env.FUSIONAUTH_CLIENT_ID!, + clientSecret: process.env.FUSIONAUTH_SECRET!, + tenantId: process.env.FUSIONAUTH_TENANT_ID + }), + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// GitHub +if(process.env.NEXT_PUBLIC_GITHUB_ENABLED === 'true') { + providers.push( + GitHubProvider({ + clientId: process.env.GITHUB_ID!, + clientSecret: process.env.GITHUB_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// GitLab +if(process.env.NEXT_PUBLIC_GITLAB_ENABLED === 'true') { + providers.push( + GitlabProvider({ + clientId: process.env.GITLAB_CLIENT_ID!, + clientSecret: process.env.GITLAB_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Google +if(process.env.NEXT_PUBLIC_GOOGLE_ENABLED === 'true') { + providers.push( + GoogleProvider({ + clientId: process.env.GOOGLE_CLIENT_ID!, + clientSecret: process.env.GOOGLE_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// HubSpot +if(process.env.NEXT_PUBLIC_HUBSPOT_ENABLED === 'true') { + providers.push( + HubspotProvider({ + clientId: process.env.HUBSPOT_CLIENT_ID!, + clientSecret: process.env.HUBSPOT_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// IdentityServer4 +if(process.env.NEXT_PUBLIC_IDS4_ENABLED === 'true') { + providers.push( + IdentityServer4Provider({ + id: "identity-server4", + name: "IdentityServer4", + issuer: process.env.IdentityServer4_Issuer, + clientId: process.env.IdentityServer4_CLIENT_ID, + clientSecret: process.env.IdentityServer4_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Kakao +if(process.env.NEXT_PUBLIC_KAKAO_ENABLED === 'true') { + providers.push( + KakaoProvider({ + clientId: process.env.KAKAO_CLIENT_ID!, + clientSecret: process.env.KAKAO_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Keycloak +if (process.env.NEXT_PUBLIC_KEYCLOAK_ENABLED === 'true') { providers.push( KeycloakProvider({ - id: "keycloak", - name: "Keycloak", clientId: process.env.KEYCLOAK_CLIENT_ID!, clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!, issuer: process.env.KEYCLOAK_ISSUER, profile: (profile) => { return { id: profile.sub, - username: profile.preferred_username, name: profile.name ?? profile.preferred_username, email: profile.email, image: profile.picture, @@ -100,37 +594,462 @@ if (keycloakEnabled) { ); const _linkAccount = adapter.linkAccount; adapter.linkAccount = (account) => { - const { "not-before-policy": _, refresh_expires_in, ...data } = account; + const {"not-before-policy": _, refresh_expires_in, ...data} = account; return _linkAccount ? _linkAccount(data) : undefined; }; } -if (authentikEnabled) { - console.log(authentikEnabled) +// LINE +if(process.env.NEXT_PUBLIC_LINE_ENABLED === 'true') { providers.push( - AuthentikProvider({ - id: "authentik", - name: "Authentik", - clientId: process.env.AUTHENTIK_CLIENT_ID!, - clientSecret: process.env.AUTHENTIK_CLIENT_SECRET!, - issuer: process.env.AUTHENTIK_ISSUER, - profile: (profile) => { - console.log(profile) - return { - id: profile.sub, - username: profile.preferred_username, - name: profile.name ?? profile.preferred_username, - email: profile.email, - image: profile.picture, - }; - }, + LineProvider({ + clientId: process.env.LINE_CLIENT_ID!, + clientSecret: process.env.LINE_CLIENT_SECRET! }) ); + const _linkAccount = adapter.linkAccount; adapter.linkAccount = (account) => { - const { "not-before-policy": _, refresh_expires_in, ...data } = account; + const {"not-before-policy": _, refresh_expires_in, ...data} = account; return _linkAccount ? _linkAccount(data) : undefined; - }; + } +} + +// LinkedIn +if(process.env.NEXT_PUBLIC_LINKEDIN_ENABLED === 'true') { + providers.push( + LinkedInProvider({ + clientId: process.env.LINKEDIN_CLIENT_ID!, + clientSecret: process.env.LINKEDIN_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Mailchimp +if(process.env.NEXT_PUBLIC_MAILCHIMP_ENABLED === 'true') { + providers.push( + MailchimpProvider({ + clientId: process.env.MAILCHIMP_CLIENT_ID, + clientSecret: process.env.MAILCHIMP_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Mail.ru +if(process.env.NEXT_PUBLIC_MAILRU_ENABLED === 'true') { + providers.push( + MailRuProvider({ + clientId: process.env.MAILRU_CLIENT_ID, + clientSecret: process.env.MAILRU_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Naver +if(process.env.NEXT_PUBLIC_NAVER_ENABLED === 'true') { + providers.push( + NaverProvider({ + clientId: process.env.NAVER_CLIENT_ID!, + clientSecret: process.env.NAVER_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Netlify +if(process.env.NEXT_PUBLIC_NETLIFY_ENABLED === 'true') { + providers.push( + NetlifyProvider({ + clientId: process.env.NETLIFY_CLIENT_ID, + clientSecret: process.env.NETLIFY_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Okta +if(process.env.NEXT_PUBLIC_OKTA_ENABLED === 'true') { + providers.push( + OktaProvider({ + clientId: process.env.OKTA_CLIENT_ID!, + clientSecret: process.env.OKTA_CLIENT_SECRET!, + issuer: process.env.OKTA_ISSUER + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// OneLogin +if(process.env.NEXT_PUBLIC_ONELOGIN_ENABLED === 'true') { + providers.push( + OneLoginProvider({ + clientId: process.env.ONELOGIN_CLIENT_ID, + clientSecret: process.env.ONELOGIN_CLIENT_SECRET, + issuer: process.env.ONELOGIN_ISSUER + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Osso +if(process.env.NEXT_PUBLIC_OSSO_ENABLED === 'true') { + providers.push( + OssoProvider({ + clientId: process.env.OSSO_CLIENT_ID, + clientSecret: process.env.OSSO_CLIENT_SECRET, + issuer: process.env.OSSO_ISSUER + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// osu! +if(process.env.NEXT_PUBLIC_OSU_ENABLED === 'true') { + providers.push( + OsuProvider({ + clientId: process.env.OSU_CLIENT_ID!, + clientSecret: process.env.OSU_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Patreon +if(process.env.NEXT_PUBLIC_PATREON_ENABLED === 'true') { + providers.push( + PatreonProvider({ + clientId: process.env.PATREON_ID!, + clientSecret: process.env.PATREON_SECRET!, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Pinterest +if(process.env.NEXT_PUBLIC_PINTEREST_ENABLED === 'true') { + providers.push( + PinterestProvider({ + clientId: process.env.PINTEREST_ID!, + clientSecret: process.env.PINTEREST_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Pipedrive +if(process.env.NEXT_PUBLIC_PIPEDRIVE_ENABLED === 'true') { + providers.push( + PipedriveProvider({ + clientId: process.env.PIPEDRIVE_CLIENT_ID!, + clientSecret: process.env.PIPEDRIVE_CLIENT_SECRET!, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Reddit +if(process.env.NEXT_PUBLIC_REDDIT_ENABLED === 'true') { + providers.push( + RedditProvider({ + clientId: process.env.REDDIT_CLIENT_ID, + clientSecret: process.env.REDDIT_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Salesforce +if(process.env.NEXT_PUBLIC_SALESFORCE_ENABLED === 'true') { + providers.push( + SalesforceProvider({ + clientId: process.env.SALESFORCE_CLIENT_ID!, + clientSecret: process.env.SALESFORCE_CLIENT_SECRET!, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Slack +if(process.env.NEXT_PUBLIC_SLACK_ENABLED === 'true') { + providers.push( + SlackProvider({ + clientId: process.env.SLACK_CLIENT_ID!, + clientSecret: process.env.SLACK_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Spotify +if(process.env.NEXT_PUBLIC_SPOTIFY_ENABLED === 'true') { + providers.push( + SpotifyProvider({ + clientId: process.env.SPOTIFY_CLIENT_ID!, + clientSecret: process.env.SPOTIFY_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Strava +if(process.env.NEXT_PUBLIC_STRAVA_ENABLED === 'true') { + providers.push( + StravaProvider({ + clientId: process.env.STRAVA_CLIENT_ID!, + clientSecret: process.env.STRAVA_CLIENT_SECRET!, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Todoist +if(process.env.NEXT_PUBLIC_TODOIST_ENABLED === 'true') { + providers.push( + TodoistProvider({ + clientId: process.env.TODOIST_ID!, + clientSecret: process.env.TODOIST_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Twitch +if(process.env.NEXT_PUBLIC_TWITCH_ENABLED === 'true') { + providers.push( + TwitchProvider({ + clientId: process.env.TWITCH_CLIENT_ID!, + clientSecret: process.env.TWITCH_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// United Effects +if(process.env.NEXT_PUBLIC_UNITED_EFFECTS_ENABLED === 'true') { + providers.push( + UnitedEffectsProvider({ + clientId: process.env.UNITED_EFFECTS_CLIENT_ID!, + clientSecret: process.env.UNITED_EFFECTS_CLIENT_SECRET!, + issuer: process.env.UNITED_EFFECTS_ISSUER! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// VK +if(process.env.NEXT_PUBLIC_VK_ENABLED === 'true') { + providers.push( + VkProvider({ + clientId: process.env.VK_CLIENT_ID!, + clientSecret: process.env.VK_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Wikimedia +if(process.env.NEXT_PUBLIC_WIKIMEDIA_ENABLED === 'true') { + providers.push( + WikimediaProvider({ + clientId: process.env.WIKIMEDIA_CLIENT_ID!, + clientSecret: process.env.WIKIMEDIA_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Wordpress.com +if(process.env.NEXT_PUBLIC_WORDPRESS_ENABLED === 'true') { + providers.push( + WordpressProvider({ + clientId: process.env.WORDPRESS_CLIENT_ID, + clientSecret: process.env.WORDPRESS_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Yandex +if(process.env.NEXT_PUBLIC_YANDEX_ENABLED === 'true') { + providers.push( + YandexProvider({ + clientId: process.env.YANDEX_CLIENT_ID!, + clientSecret: process.env.YANDEX_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Zitadel +if(process.env.NEXT_PUBLIC_ZITADEL_ENABLED === 'true') { + providers.push( + ZitadelProvider({ + issuer: process.env.ZITADEL_ISSUER, + clientId: process.env.ZITADEL_CLIENT_ID!, + clientSecret: process.env.ZITADEL_CLIENT_SECRET!, + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Zoho +if(process.env.NEXT_PUBLIC_ZOHO_ENABLED === 'true') { + providers.push( + ZohoProvider({ + clientId: process.env.ZOHO_CLIENT_ID, + clientSecret: process.env.ZOHO_CLIENT_SECRET + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } +} + +// Zoom +if(process.env.NEXT_PUBLIC_ZOOM_ENABLED_ENABLED === 'true') { + providers.push( + ZoomProvider({ + clientId: process.env.ZOOM_CLIENT_ID!, + clientSecret: process.env.ZOOM_CLIENT_SECRET! + }) + ); + + const _linkAccount = adapter.linkAccount; + adapter.linkAccount = (account) => { + const {"not-before-policy": _, refresh_expires_in, ...data} = account; + return _linkAccount ? _linkAccount(data) : undefined; + } } export const authOptions: AuthOptions = { @@ -145,14 +1064,27 @@ export const authOptions: AuthOptions = { verifyRequest: "/confirmation", }, callbacks: { - async jwt({ token, trigger, user }) { + async signIn({user, account, profile, email, credentials}) { + if (account?.provider !== "credentials") { // registration via SSO can be separately disabled + const existingUser = await prisma.account.findFirst({ + where: { + providerAccountId: account?.providerAccountId, + }, + }); + if (existingUser && newSsoUsersDisabled) { + return false; + } + } + return true; + }, + async jwt({token, trigger, user}) { token.sub = token.sub ? Number(token.sub) : undefined; if (trigger === "signIn" || trigger === "signUp") token.id = user?.id as number; return token; }, - async session({ session, token }) { + async session({session, token}) { session.user.id = token.id; if (STRIPE_SECRET_KEY) { diff --git a/pages/api/v1/logins/index.ts b/pages/api/v1/logins/index.ts new file mode 100644 index 0000000..04a7fdd --- /dev/null +++ b/pages/api/v1/logins/index.ts @@ -0,0 +1,31 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import * as process from "process"; + +export type ResponseData = { + credentialsEnabled: string|undefined + emailEnabled: string|undefined + registrationDisabled: string|undefined + buttonAuths: { + method: string + name: string + }[] +} +export default function handler(req: NextApiRequest, res: NextApiResponse) { + res.json(getLogins()); +} + +export function getLogins() { + const buttonAuths = [] + if (process.env.NEXT_PUBLIC_KEYCLOAK_ENABLED === 'true') { + buttonAuths.push({method: 'keycloak', name: 'Keycloak'}); + } + if (process.env.NEXT_PUBLIC_AUTHENTIK_ENABLED === 'true') { + buttonAuths.push({method: 'authentik', name: process.env.AUTHENTIK_CUSTOM_NAME ?? 'Authentik'}); + } + return { + credentialsEnabled: (process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === 'true' || process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === undefined) ? "true" : "false", + emailEnabled: process.env.NEXT_PUBLIC_EMAIL_PROVIDER, + registrationDisabled: process.env.NEXT_PUBLIC_DISABLE_REGISTRATION, + buttonAuths: buttonAuths + }; +} \ No newline at end of file diff --git a/pages/login.tsx b/pages/login.tsx index 122c55f..3cee3de 100644 --- a/pages/login.tsx +++ b/pages/login.tsx @@ -5,17 +5,21 @@ import { signIn } from "next-auth/react"; import Link from "next/link"; import { useState, FormEvent } from "react"; import { toast } from "react-hot-toast"; +import {getLogins} from './api/v1/logins' +import {InferGetServerSidePropsType} from "next"; interface FormData { username: string; password: string; } -const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER; -const keycloakEnabled = process.env.NEXT_PUBLIC_KEYCLOAK_ENABLED; -const authentikEnabled = process.env.NEXT_PUBLIC_AUTHENTIK_ENABLED; +export const getServerSideProps = (() => { + const availableLogins = getLogins(); + return {props: {availableLogins}} +}); -export default function Login() { + +export default function Login({availableLogins} : InferGetServerSidePropsType) { const [submitLoader, setSubmitLoader] = useState(false); const [form, setForm] = useState({ @@ -49,118 +53,106 @@ export default function Login() { } } - async function loginUserKeycloak() { + async function loginUserButton(method: string) { setSubmitLoader(true); const load = toast.loading("Authenticating..."); - const res = await signIn("keycloak", {}); + const res = await signIn(method, {}); toast.dismiss(load); setSubmitLoader(false); } - async function loginUserAuthentik() { - setSubmitLoader(true); + function displayLoginCredential() { + if (availableLogins.credentialsEnabled === 'true') { + return (<>

+ Enter your credentials +

+
+
+

+ Username + {availableLogins.emailEnabled === 'true' ? " or Email" : undefined} +

- const load = toast.loading("Authenticating..."); + setForm({...form, username: e.target.value})}/> +
+
+

+ Password +

- const res = await signIn("authentik", {}); + setForm({...form, password: e.target.value})}/> + {availableLogins.emailEnabled === 'true' && ( +
+ + Forgot Password? + +
+ )} +
+ + ) + } + } + function displayLoginExternalButton() { + const Buttons: any = []; + availableLogins.buttonAuths.forEach((value, index) => { + Buttons.push( loginUserButton(value.method)} + label={`Sign in with ${value.name}`} + className=" w-full text-center" + loading={submitLoader} + />); + }); + return (Buttons); + } - toast.dismiss(load); - - setSubmitLoader(false); + function displayRegistration() { + if (availableLogins.registrationDisabled !== 'true') { + return ( +
+

+ New here? +

+ + Sign Up + +
+ ); + } } return (
-
- {process.env.NEXT_PUBLIC_DISABLE_LOGIN !== "true" ? ( -
-

- Enter your credentials -

- -
- -
-

- Username - {emailEnabled ? " or Email" : undefined} -

- - - setForm({ ...form, username: e.target.value }) - } - /> -
- -
-

Password

- - - setForm({ ...form, password: e.target.value }) - } - /> - {emailEnabled && ( -
- - Forgot Password? - -
- )} -
- - -
- ) : undefined} - {process.env.NEXT_PUBLIC_KEYCLOAK_ENABLED === "true" ? ( - - ) : undefined} - {process.env.NEXT_PUBLIC_AUTHENTIK_ENABLED === "true" ? ( - - ) : undefined} - {process.env.NEXT_PUBLIC_DISABLE_REGISTRATION === - "true" ? undefined : ( -
-

New here?

- - Sign Up - -
- )} +
+ {displayLoginCredential()} + {displayLoginExternalButton()} + {displayRegistration()}
diff --git a/pages/register.tsx b/pages/register.tsx index d8b0650..e1944bd 100644 --- a/pages/register.tsx +++ b/pages/register.tsx @@ -1,13 +1,12 @@ import Link from "next/link"; import { useState, FormEvent } from "react"; import { toast } from "react-hot-toast"; -import SubmitButton from "@/components/SubmitButton"; import { signIn } from "next-auth/react"; import { useRouter } from "next/router"; import CenteredForm from "@/layouts/CenteredForm"; import TextInput from "@/components/TextInput"; -const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER; +const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER === "true"; type FormData = { name: string; diff --git a/types/enviornment.d.ts b/types/enviornment.d.ts index 4ef9d77..2c4fef6 100644 --- a/types/enviornment.d.ts +++ b/types/enviornment.d.ts @@ -9,7 +9,6 @@ declare global { STORAGE_FOLDER?: string; AUTOSCROLL_TIMEOUT?: string; RE_ARCHIVE_LIMIT?: string; - NEXT_PUBLIC_MAX_UPLOAD_SIZE?: string; SPACES_KEY?: string; SPACES_SECRET?: string; @@ -18,24 +17,383 @@ declare global { SPACES_REGION?: string; SPACES_FORCE_PATH_STYLE?: string; - NEXT_PUBLIC_KEYCLOAK_ENABLED?: string; - KEYCLOAK_ISSUER?: string; - KEYCLOAK_CLIENT_ID?: string; - KEYCLOAK_CLIENT_SECRET?: string; + NEXT_PUBLIC_CREDENTIALS_ENABLED?: string; + DISABLE_NEW_SSO_USERS?: string; NEXT_PUBLIC_EMAIL_PROVIDER?: string; EMAIL_FROM?: string; EMAIL_SERVER?: string; - NEXT_PUBLIC_STRIPE?: string; - STRIPE_SECRET_KEY?: string; - MONTHLY_PRICE_ID?: string; - YEARLY_PRICE_ID?: string; - NEXT_PUBLIC_STRIPE_BILLING_PORTAL_URL?: string; - NEXT_PUBLIC_TRIAL_PERIOD_DAYS?: string; - BASE_URL?: string; + NEXT_PUBLIC_STRIPE?: string; + STRIPE_SECRET_KEY?: string; + MONTHLY_PRICE_ID?: string; + YEARLY_PRICE_ID?: string; + NEXT_PUBLIC_STRIPE_BILLING_PORTAL_URL?: string; + NEXT_PUBLIC_TRIAL_PERIOD_DAYS?: string; + BASE_URL?: string; + + // + // SSO Providers + // + + // 42 School + NEXT_PUBLIC_FORTYTWO_ENABLED?: string; + FORTYTWO_CUSTOM_NAME?: string; + FORTYTWO_CLIENT_ID?: string; + FORTYTWO_CLIENT_SECRET?: string; + + // Apple + NEXT_PUBLIC_APPLE_ENABLED?: string; + APPLE_CUSTOM_NAME?: string; + APPLE_ID?: string; + APPLE_SECRET?: string; + + // Atlassian + NEXT_PUBLIC_ATLASSIAN_ENABLED?: string; + ATLASSIAN_CUSTOM_NAME?: string; + ATLASSIAN_CLIENT_ID?: string; + ATLASSIAN_CLIENT_SECRET?: string; + ATLASSIAN_SCOPE?: string; + + // Auth0 + NEXT_PUBLIC_AUTH0_ENABLED?: string; + AUTH0_CUSTOM_NAME?: string; + AUTH0_ISSUER?: string; + AUTH0_CLIENT_SECRET?: string; + AUTH0_CLIENT_ID?: string; + + // Authentik + NEXT_PUBLIC_AUTHENTIK_ENABLED?: string; + AUTHENTIK_CUSTOM_NAME?: string; + AUTHENTIK_ISSUER?: string; + AUTHENTIK_CLIENT_ID?: string; + AUTHENTIK_CLIENT_SECRET?: string; + + // TODO: Azure AD B2C + // TODO: Azure AD + + // Battle.net + NEXT_PUBLIC_BATTLENET_ENABLED?: string; + BATTLENET_CUSTOM_NAME?: string; + BATTLENET_CLIENT_ID?: string; + BATTLENET_CLIENT_SECRET?: string; + BATLLENET_ISSUER?: string; + + // Box + NEXT_PUBLIC_BOX_ENABLED?: string; + BOX_CUSTOM_NAME?: string; + BOX_CLIENT_ID?: string; + BOX_CLIENT_SECRET?: string; + + // TODO: BoxyHQ SAML + + // Bungie + NEXT_PUBLIC_BUNGIE_ENABLED?: string; + BUNGIE_CUSTOM_NAME?: string; + BUNGIE_CLIENT_ID?: string; + BUNGIE_CLIENT_SECRET?: string; + BUNGIE_API_KEY?: string; + + // Cognito + NEXT_PUBLIC_COGNITO_ENABLED?: string; + COGNITO_CUSTOM_NAME?: string; + COGNITO_CLIENT_ID?: string; + COGNITO_CLIENT_SECRET?: string; + COGNITO_ISSUER?: string; + + // Coinbase + NEXT_PUBLIC_COINBASE_ENABLED?: string; + COINBASE_CUSTOM_NAME?: string; + COINBASE_CLIENT_ID?: string; + COINBASE_CLIENT_SECRET?: string; + + // Discord + NEXT_PUBLIC_DISCORD_ENABLED?: string; + DISCORD_CUSTOM_NAME?: string; + DISCORD_CLIENT_ID?: string; + DISCORD_CLIENT_SECRET?: string; + + // Dropbox + NEXT_PUBLIC_DROPBOX_ENABLED?: string; + DROPBOX_CUSTOM_NAME?: string; + DROPBOX_CLIENT_ID?: string; + DROPBOX_CLIENT_SECRET?: string; + + // DuendeIndentityServer6 + NEXT_PUBLIC_DUENDE_IDS6_ENABLED?: string; + DUENDE_IDS6_CUSTOM_NAME?: string; + DUENDE_IDS6_CLIENT_ID?: string; + DUENDE_IDS6_CLIENT_SECRET?: string; + DUENDE_IDS6_ISSUER?: string; + + // EVE Online + NEXT_PUBLIC_EVEONLINE_ENABLED?: string; + EVEONLINE_CUSTOM_NAME?: string; + EVEONLINE_CLIENT_ID?: string; + EVEONLINE_CLIENT_SECRET?: string; + + // Facebook + NEXT_PUBLIC_FACEBOOK_ENABLED?: string; + FACEBOOK_CUSTOM_NAME?: string; + FACEBOOK_CLIENT_ID?: string; + FACEBOOK_CLIENT_SECRET?: string; + + // FACEIT + NEXT_PUBLIC_FACEIT_ENABLED?: string; + FACEIT_CUSTOM_NAME?: string; + FACEIT_CLIENT_ID?: string; + FACEIT_CLIENT_SECRET?: string; + + // Foursquare + NEXT_PUBLIC_FOURSQUARE_ENABLED?: string; + FOURSQUARE_CUSTOM_NAME?: string; + FOURSQUARE_CLIENT_ID?: string; + FOURSQUARE_CLIENT_SECRET?: string; + FOURSQUARE_APIVERSION?: string; + + // Freshbooks + NEXT_PUBLIC_FRESHBOOKS_ENABLED?: string; + FRESHBOOKS_CUSTOM_NAME?: string; + FRESHBOOKS_CLIENT_ID?: string; + FRESHBOOKS_CLIENT_SECRET?: string; + + // FusionAuth + NEXT_PUBLIC_FUSIONAUTH_ENABLED?: string; + FUSIONAUTH_CUSTOM_NAME?: string; + FUSIONAUTH_CLIENT_ID?: string; + FUSIONAUTH_CLIENT_SECRET?: string; + FUSIONAUTH_ISSUER?: string; + FUSIONAUTH_TENANT_ID?: string; + + // GitHub + NEXT_PUBLIC_GITHUB_ENABLED?: string; + GITHUB_CUSTOM_NAME?: string; + GITHUB_CLIENT_ID?: string; + GITHUB_CLIENT_SECRET?: string; + + // GitLab + NEXT_PUBLIC_GITLAB_ENABLED?: string; + GITLAB_CUSTOM_NAME?: string; + GITLAB_CLIENT_ID?: string; + GITLAB_CLIENT_SECRET?: string; + + // Google + NEXT_PUBLIC_GOOGLE_ENABLED?: string; + NEXT_PUBLIC_GOOGLE_CUSTOM_NAME?: string; + GOOGLE_CLIENT_ID?: string; + GOOGLE_CLIENT_SECRET?: string; + + // HubSpot + NEXT_PUBLIC_HUBSPOT_ENABLED?: string; + HUBSPOT_CUSTOM_NAME?: string; + HUBSPOT_CLIENT_ID?: string; + HUBSPOT_CLIENT_SECRET?: string; + + // IdentityServer4 + NEXT_PUBLIC_IDS4_ENABLED?: string; + IDS4_CUSTOM_NAME?: string; + IDS4_CLIENT_ID?: string; + IDS4_CLIENT_SECRET?: string; + IDS4_ISSUER?: string; + + // TODO: Instagram (Doesn't return email) + + // Kakao + NEXT_PUBLIC_KAKAO_ENABLED?: string; + KAOKAO_CUSTOM_NAME?: string; + KAKAO_CLIENT_ID?: string; + KAKAO_CLIENT_SECRET?: string; + + // Keycloak + NEXT_PUBLIC_KEYCLOAK_ENABLED?: string; + KEYCLOAK_CUSTOM_NAME?: string; + KEYCLOAK_ISSUER?: string; + KEYCLOAK_CLIENT_ID?: string; + KEYCLOAK_CLIENT_SECRET?: string; + + // LINE + NEXT_PUBLIC_LINE_ENABLED?: string; + LINE_CUSTOM_NAME?: string; + LINE_CLIENT_ID?: string; + LINE_CLIENT_SECRET?: string; + + // LinkedIn + NEXT_PUBLIC_LINKEDIN_ENABLED?: string; + LINKEDIN_CUSTOM_NAME?: string; + LINKEDIN_CLIENT_ID?: string; + LINKEDIN_CLIENT_SECRET?: string; + + // Mailchimp + NEXT_PUBLIC_MAILCHIMP_ENABLED?: string; + MAILCHIMP_CUSTOM_NAME?: string; + MAILCHIMP_CLIENT_ID?: string; + MAILCHIMP_CLIENT_SECRET?: string; + + // Mail.ru + NEXT_PUBLIC_MAILRU_ENABLED?: string; + MAILRU_CUSTOM_NAME?: string; + MAILRU_CLIENT_ID?: string; + MAILRU_CLIENT_SECRET?: string; + + // TODO: Medium (Doesn't return email) + + // Naver + NEXT_PUBLIC_NAVER_ENABLED?: string; + NAVER_CUSTOM_NAME?: string; + NAVER_CLIENT_ID?: string; + NAVER_CLIENT_SECRET?: string; + + // Netlify + NEXT_PUBLIC_NETLIFY_ENABLED?: string; + NETLIFY_CUSTOM_NAME?: string; + NETLIFY_CLIENT_ID?: string; + NETLIFY_CLIENT_SECRET?: string; + + // Okta + NEXT_PUBLIC_OKTA_ENABLED?: string; + OKTA_CUSTOM_NAME?: string; + OKTA_CLIENT_ID?: string; + OKTA_CLIENT_SECRET?: string; + OKTA_ISSUER?: string; + + // OneLogin + NEXT_PUBLIC_ONELOGIN_ENABLED?: string; + ONELOGIN_CUSTOM_NAME?: string; + ONELOGIN_CLIENT_ID?: string; + ONELOGIN_CLIENT_SECRET?: string; + ONELOGIN_ISSUER?: string; + + // Osso + NEXT_PUBLIC_OSSO_ENABLED?: string; + OSSO_CUSTOM_NAME?: string; + OSSO_CLIENT_ID?: string; + OSSO_CLIENT_SECRET?: string; + OSSO_ISSUER?: string; + + // osu! + NEXT_PUBLIC_OSU_ENABLED?: string; + OSU_CUSTOM_NAME?: string; + OSU_CLIENT_ID?: string; + OSU_CLIENT_SECRET?: string; + + // Patreon + NEXT_PUBLIC_PATREON_ENABLED?: string; + PATREON_CUSTOM_NAME?: string; + PATREON_CLIENT_ID?: string; + PATREON_CLIENT_SECRET?: string; + + // Pinterest + NEXT_PUBLIC_PINTEREST_ENABLED?: string; + PINTEREST_CUSTOM_NAME?: string; + PINTEREST_CLIENT_ID?: string; + PINTEREST_CLIENT_SECRET?: string; + + // Pipedrive + NEXT_PUBLIC_PIPEDRIVE_ENABLED?: string; + PIPEDRIVE_CUSTOM_NAME?: string; + PIPEDRIVE_CLIENT_ID?: string; + PIPEDRIVE_CLIENT_SECRET?: string; + + // Reddit + // TODO (1h tokens) + NEXT_PUBLIC_REDDIT_ENABLED?: string; + REDDIT_CUSTOM_NAME?: string; + REDDIT_CLIENT_ID?: string; + REDDIT_CLIENT_SECRET?: string; + + // Salesforce + NEXT_PUBLIC_SALESFORCE_ENABLED?: string; + SALESFORCE_CUSTOM_NAME?: string; + SALESFORCE_CLIENT_ID?: string; + SALESFORCE_CLIENT_SECRET?: string; + + // Slack + NEXT_PUBLIC_SLACK_ENABLED?: string; + SLACK_CUSTOM_NAME?: string; + SLACK_CLIENT_ID?: string; + SLACK_CLIENT_SECRET?: string; + + // Spotify + NEXT_PUBLIC_SPOTIFY_ENABLED?: string; + SPOTIFY_CUSTOM_NAME?: string; + SPOTIFY_CLIENT_ID?: string; + SPOTIFY_CLIENT_SECRET?: string; + + // Strava + NEXT_PUBLIC_STRAVA_ENABLED?: string; + STRAVA_CUSTOM_NAME?: string; + STRAVA_CLIENT_ID?: string; + STRAVA_CLIENT_SECRET?: string; + + // Todoist + NEXT_PUBLIC_TODOIST_ENABLED?: string; + TODOIST_CUSTOM_NAME?: string; + TODOIST_CLIENT_ID?: string; + TODOIST_CLIENT_SECRET?: string; + + // TODO: Trakt (Doesn't return email) + + // Twitch + NEXT_PUBLIC_TWITCH_ENABLED?: string; + TWITCH_CUSTOM_NAME?: string; + TWITCH_CLIENT_ID?: string; + TWITCH_CLIENT_SECRET?: string; + + // TODO: Twitter (OAuth 1.0) + + // United Effects + NEXT_PUBLIC_UNITED_EFFECTS_ENABLED?: string; + UNITED_EFFECTS_CUSTOM_NAME?: string; + UNITED_EFFECTS_CLIENT_ID?: string; + UNITED_EFFECTS_CLIENT_SECRET?: string; + UNITED_EFFECTS_ISSUER?: string; + + // VK + NEXT_PUBLIC_VK_ENABLED?: string; + VK_CUSTOM_NAME?: string; + VK_CLIENT_ID?: string; + VK_CLIENT_SECRET?: string; + + // Wikimedia + NEXT_PUBLIC_WIKIMEDIA_ENABLED?: string; + WIKIMEDIA_CUSTOM_NAME?: string; + WIKIMEDIA_CLIENT_ID?: string; + WIKIMEDIA_CLIENT_SECRET?: string; + + // Wordpress.com + NEXT_PUBLIC_WORDPRESS_ENABLED?: string; + WORDPRESS_CUSTOM_NAME?: string; + WORDPRESS_CLIENT_ID?: string; + WORDPRESS_CLIENT_SECRET?: string; + + // TODO: WorkOS (Custom flow) + + // Yandex + NEXT_PUBLIC_YANDEX_ENABLED?: string; + YANDEX_CUSTOM_NAME?: string; + YANDEX_CLIENT_ID?: string; + YANDEX_CLIENT_SECRET?: string; + + // Zitadel + NEXT_PUBLIC_ZITADEL_ENABLED?: string; + ZITADEL_CUSTOM_NAME?: string; + ZITADEL_CLIENT_ID?: string; + ZITADEL_CLIENT_SECRET?: string; + ZITADEL_ISSUER?: string; + + // Zoho + NEXT_PUBLIC_ZOHO_ENABLED?: string; + ZOHO_CUSTOM_NAME?: string; + ZOHO_CLIENT_ID?: string; + ZOHO_CLIENT_SECRET?: string; + + // Zoom + NEXT_PUBLIC_ZOOM_ENABLED?: string; + ZOOM_CUSTOM_NAME?: string; + ZOOM_CLIENT_ID?: string; + ZOOM_CLIENT_SECRET?: string; + } } - } } export {}; From 3f4b7117bde905ee9b0e69877e07f67941644d56 Mon Sep 17 00:00:00 2001 From: Sebastian Hierholzer Date: Sun, 3 Dec 2023 22:19:02 +0100 Subject: [PATCH 4/7] fix: add getLogins boilerplate --- pages/api/v1/logins/index.ts | 211 ++++++++++++++++++++++++++++++++++- types/enviornment.d.ts | 2 +- 2 files changed, 210 insertions(+), 3 deletions(-) diff --git a/pages/api/v1/logins/index.ts b/pages/api/v1/logins/index.ts index 04a7fdd..c592451 100644 --- a/pages/api/v1/logins/index.ts +++ b/pages/api/v1/logins/index.ts @@ -16,12 +16,219 @@ export default function handler(req: NextApiRequest, res: NextApiResponse Date: Thu, 7 Dec 2023 13:25:34 +0100 Subject: [PATCH 5/7] fix: undefined variables failing to parse --- pages/api/v1/logins/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/api/v1/logins/index.ts b/pages/api/v1/logins/index.ts index c592451..b5efab8 100644 --- a/pages/api/v1/logins/index.ts +++ b/pages/api/v1/logins/index.ts @@ -231,8 +231,8 @@ export function getLogins() { } return { credentialsEnabled: (process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === 'true' || process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === undefined) ? "true" : "false", - emailEnabled: process.env.NEXT_PUBLIC_EMAIL_PROVIDER, - registrationDisabled: process.env.NEXT_PUBLIC_DISABLE_REGISTRATION, + emailEnabled: (process.env.NEXT_PUBLIC_EMAIL_PROVIDER === 'true' ? 'true' : 'false'), + registrationDisabled: (process.env.NEXT_PUBLIC_DISABLE_REGISTRATION === 'true' ? 'true' : 'false'), buttonAuths: buttonAuths }; } \ No newline at end of file From c367992116f776b05fe476be123e476b53870252 Mon Sep 17 00:00:00 2001 From: Sebastian Hierholzer Date: Thu, 7 Dec 2023 13:26:34 +0100 Subject: [PATCH 6/7] fix: update .env.sample --- .env.sample | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 341 insertions(+), 6 deletions(-) diff --git a/.env.sample b/.env.sample index f8fc24e..daf5c94 100644 --- a/.env.sample +++ b/.env.sample @@ -12,7 +12,8 @@ PAGINATION_TAKE_COUNT= STORAGE_FOLDER= AUTOSCROLL_TIMEOUT= NEXT_PUBLIC_DISABLE_REGISTRATION= -NEXT_PUBLIC_DISABLE_LOGIN= +NEXT_PUBLIC_CREDENTIALS_ENABLED= +DISABLE_NEW_SSO_USERS= RE_ARCHIVE_LIMIT= NEXT_PUBLIC_MAX_UPLOAD_SIZE= @@ -29,14 +30,348 @@ NEXT_PUBLIC_EMAIL_PROVIDER= EMAIL_FROM= EMAIL_SERVER= + +# +# SSO Providers +# + +# 42 School +NEXT_PUBLIC_FORTYTWO_ENABLED= +FORTYTWO_CUSTOM_NAME= +FORTYTWO_CLIENT_ID= +FORTYTWO_CLIENT_SECRET= + +# Apple +NEXT_PUBLIC_APPLE_ENABLED= +APPLE_CUSTOM_NAME= +APPLE_ID= +APPLE_SECRET= + +# Atlassian +NEXT_PUBLIC_ATLASSIAN_ENABLED= +ATLASSIAN_CUSTOM_NAME= +ATLASSIAN_CLIENT_ID= +ATLASSIAN_CLIENT_SECRET= +ATLASSIAN_SCOPE= + +# Auth0 +NEXT_PUBLIC_AUTH0_ENABLED= +AUTH0_CUSTOM_NAME= +AUTH0_ISSUER= +AUTH0_CLIENT_SECRET= +AUTH0_CLIENT_ID= + +# Authentik +NEXT_PUBLIC_AUTHENTIK_ENABLED= +AUTHENTIK_CUSTOM_NAME= +AUTHENTIK_ISSUER= +AUTHENTIK_CLIENT_ID= +AUTHENTIK_CLIENT_SECRET= + +# Battle.net +NEXT_PUBLIC_BATTLENET_ENABLED= +BATTLENET_CUSTOM_NAME= +BATTLENET_CLIENT_ID= +BATTLENET_CLIENT_SECRET= +BATLLENET_ISSUER= + +# Box +NEXT_PUBLIC_BOX_ENABLED= +BOX_CUSTOM_NAME= +BOX_CLIENT_ID= +BOX_CLIENT_SECRET= + +# Bungie +NEXT_PUBLIC_BUNGIE_ENABLED= +BUNGIE_CUSTOM_NAME= +BUNGIE_CLIENT_ID= +BUNGIE_CLIENT_SECRET= +BUNGIE_API_KEY= + +# Cognito +NEXT_PUBLIC_COGNITO_ENABLED= +COGNITO_CUSTOM_NAME= +COGNITO_CLIENT_ID= +COGNITO_CLIENT_SECRET= +COGNITO_ISSUER= + +# Coinbase +NEXT_PUBLIC_COINBASE_ENABLED= +COINBASE_CUSTOM_NAME= +COINBASE_CLIENT_ID= +COINBASE_CLIENT_SECRET= + +# Discord +NEXT_PUBLIC_DISCORD_ENABLED= +DISCORD_CUSTOM_NAME= +DISCORD_CLIENT_ID= +DISCORD_CLIENT_SECRET= + +# Dropbox +NEXT_PUBLIC_DROPBOX_ENABLED= +DROPBOX_CUSTOM_NAME= +DROPBOX_CLIENT_ID= +DROPBOX_CLIENT_SECRET= + +# DuendeIndentityServer6 +NEXT_PUBLIC_DUENDE_IDS6_ENABLED= +DUENDE_IDS6_CUSTOM_NAME= +DUENDE_IDS6_CLIENT_ID= +DUENDE_IDS6_CLIENT_SECRET= +DUENDE_IDS6_ISSUER= + +# EVE Online +NEXT_PUBLIC_EVEONLINE_ENABLED= +EVEONLINE_CUSTOM_NAME= +EVEONLINE_CLIENT_ID= +EVEONLINE_CLIENT_SECRET= + +# Facebook +NEXT_PUBLIC_FACEBOOK_ENABLED= +FACEBOOK_CUSTOM_NAME= +FACEBOOK_CLIENT_ID= +FACEBOOK_CLIENT_SECRET= + +# FACEIT +NEXT_PUBLIC_FACEIT_ENABLED= +FACEIT_CUSTOM_NAME= +FACEIT_CLIENT_ID= +FACEIT_CLIENT_SECRET= + +# Foursquare +NEXT_PUBLIC_FOURSQUARE_ENABLED= +FOURSQUARE_CUSTOM_NAME= +FOURSQUARE_CLIENT_ID= +FOURSQUARE_CLIENT_SECRET= +FOURSQUARE_APIVERSION= + +# Freshbooks +NEXT_PUBLIC_FRESHBOOKS_ENABLED= +FRESHBOOKS_CUSTOM_NAME= +FRESHBOOKS_CLIENT_ID= +FRESHBOOKS_CLIENT_SECRET= + +# FusionAuth +NEXT_PUBLIC_FUSIONAUTH_ENABLED= +FUSIONAUTH_CUSTOM_NAME= +FUSIONAUTH_CLIENT_ID= +FUSIONAUTH_CLIENT_SECRET= +FUSIONAUTH_ISSUER= +FUSIONAUTH_TENANT_ID= + +# GitHub +NEXT_PUBLIC_GITHUB_ENABLED= +GITHUB_CUSTOM_NAME= +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= + +# GitLab +NEXT_PUBLIC_GITLAB_ENABLED= +GITLAB_CUSTOM_NAME= +GITLAB_CLIENT_ID= +GITLAB_CLIENT_SECRET= + +# Google +NEXT_PUBLIC_GOOGLE_ENABLED= +GOOGLE_CUSTOM_NAME= +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= + +# HubSpot +NEXT_PUBLIC_HUBSPOT_ENABLED= +HUBSPOT_CUSTOM_NAME= +HUBSPOT_CLIENT_ID= +HUBSPOT_CLIENT_SECRET= + +# IdentityServer4 +NEXT_PUBLIC_IDS4_ENABLED= +IDS4_CUSTOM_NAME= +IDS4_CLIENT_ID= +IDS4_CLIENT_SECRET= +IDS4_ISSUER= + +# Kakao +NEXT_PUBLIC_KAKAO_ENABLED= +KAOKAO_CUSTOM_NAME= +KAKAO_CLIENT_ID= +KAKAO_CLIENT_SECRET= + # Keycloak NEXT_PUBLIC_KEYCLOAK_ENABLED= +KEYCLOAK_CUSTOM_NAME= KEYCLOAK_ISSUER= KEYCLOAK_CLIENT_ID= KEYCLOAK_CLIENT_SECRET= -# Authentik -NEXT_PUBLIC_AUTHENTIK_ENABLED= -AUTHENTIK_ISSUER= -AUTHENTIK_CLIENT_ID= -AUTHENTIK_CLIENT_SECRET= +# LINE +NEXT_PUBLIC_LINE_ENABLED= +LINE_CUSTOM_NAME= +LINE_CLIENT_ID= +LINE_CLIENT_SECRET= + +# LinkedIn +NEXT_PUBLIC_LINKEDIN_ENABLED= +LINKEDIN_CUSTOM_NAME= +LINKEDIN_CLIENT_ID= +LINKEDIN_CLIENT_SECRET= + +# Mailchimp +NEXT_PUBLIC_MAILCHIMP_ENABLED= +MAILCHIMP_CUSTOM_NAME= +MAILCHIMP_CLIENT_ID= +MAILCHIMP_CLIENT_SECRET= + +# Mail.ru +NEXT_PUBLIC_MAILRU_ENABLED= +MAILRU_CUSTOM_NAME= +MAILRU_CLIENT_ID= +MAILRU_CLIENT_SECRET= + +# Naver +NEXT_PUBLIC_NAVER_ENABLED= +NAVER_CUSTOM_NAME= +NAVER_CLIENT_ID= +NAVER_CLIENT_SECRET= + +# Netlify +NEXT_PUBLIC_NETLIFY_ENABLED= +NETLIFY_CUSTOM_NAME= +NETLIFY_CLIENT_ID= +NETLIFY_CLIENT_SECRET= + +# Okta +NEXT_PUBLIC_OKTA_ENABLED= +OKTA_CUSTOM_NAME= +OKTA_CLIENT_ID= +OKTA_CLIENT_SECRET= +OKTA_ISSUER= + +# OneLogin +NEXT_PUBLIC_ONELOGIN_ENABLED= +ONELOGIN_CUSTOM_NAME= +ONELOGIN_CLIENT_ID= +ONELOGIN_CLIENT_SECRET= +ONELOGIN_ISSUER= + +# Osso +NEXT_PUBLIC_OSSO_ENABLED= +OSSO_CUSTOM_NAME= +OSSO_CLIENT_ID= +OSSO_CLIENT_SECRET= +OSSO_ISSUER= + +# osu! +NEXT_PUBLIC_OSU_ENABLED= +OSU_CUSTOM_NAME= +OSU_CLIENT_ID= +OSU_CLIENT_SECRET= + +# Patreon +NEXT_PUBLIC_PATREON_ENABLED= +PATREON_CUSTOM_NAME= +PATREON_CLIENT_ID= +PATREON_CLIENT_SECRET= + +# Pinterest +NEXT_PUBLIC_PINTEREST_ENABLED= +PINTEREST_CUSTOM_NAME= +PINTEREST_CLIENT_ID= +PINTEREST_CLIENT_SECRET= + +# Pipedrive +NEXT_PUBLIC_PIPEDRIVE_ENABLED= +PIPEDRIVE_CUSTOM_NAME= +PIPEDRIVE_CLIENT_ID= +PIPEDRIVE_CLIENT_SECRET= + +# Reddit +NEXT_PUBLIC_REDDIT_ENABLED= +REDDIT_CUSTOM_NAME= +REDDIT_CLIENT_ID= +REDDIT_CLIENT_SECRET= + +# Salesforce +NEXT_PUBLIC_SALESFORCE_ENABLED= +SALESFORCE_CUSTOM_NAME= +SALESFORCE_CLIENT_ID= +SALESFORCE_CLIENT_SECRET= + +# Slack +NEXT_PUBLIC_SLACK_ENABLED= +SLACK_CUSTOM_NAME= +SLACK_CLIENT_ID= +SLACK_CLIENT_SECRET= + +# Spotify +NEXT_PUBLIC_SPOTIFY_ENABLED= +SPOTIFY_CUSTOM_NAME= +SPOTIFY_CLIENT_ID= +SPOTIFY_CLIENT_SECRET= + +# Strava +NEXT_PUBLIC_STRAVA_ENABLED= +STRAVA_CUSTOM_NAME= +STRAVA_CLIENT_ID= +STRAVA_CLIENT_SECRET= + +# Todoist +NEXT_PUBLIC_TODOIST_ENABLED= +TODOIST_CUSTOM_NAME= +TODOIST_CLIENT_ID= +TODOIST_CLIENT_SECRET= + +# Twitch +NEXT_PUBLIC_TWITCH_ENABLED= +TWITCH_CUSTOM_NAME= +TWITCH_CLIENT_ID= +TWITCH_CLIENT_SECRET= + +# United Effects +NEXT_PUBLIC_UNITED_EFFECTS_ENABLED= +UNITED_EFFECTS_CUSTOM_NAME= +UNITED_EFFECTS_CLIENT_ID= +UNITED_EFFECTS_CLIENT_SECRET= +UNITED_EFFECTS_ISSUER= + +# VK +NEXT_PUBLIC_VK_ENABLED= +VK_CUSTOM_NAME= +VK_CLIENT_ID= +VK_CLIENT_SECRET= + +# Wikimedia +NEXT_PUBLIC_WIKIMEDIA_ENABLED= +WIKIMEDIA_CUSTOM_NAME= +WIKIMEDIA_CLIENT_ID= +WIKIMEDIA_CLIENT_SECRET= + +# Wordpress.com +NEXT_PUBLIC_WORDPRESS_ENABLED= +WORDPRESS_CUSTOM_NAME= +WORDPRESS_CLIENT_ID= +WORDPRESS_CLIENT_SECRET= + +# Yandex +NEXT_PUBLIC_YANDEX_ENABLED= +YANDEX_CUSTOM_NAME= +YANDEX_CLIENT_ID= +YANDEX_CLIENT_SECRET= + +# Zitadel +NEXT_PUBLIC_ZITADEL_ENABLED= +ZITADEL_CUSTOM_NAME= +ZITADEL_CLIENT_ID= +ZITADEL_CLIENT_SECRET= +ZITADEL_ISSUER= + +# Zoho +NEXT_PUBLIC_ZOHO_ENABLED= +ZOHO_CUSTOM_NAME= +ZOHO_CLIENT_ID= +ZOHO_CLIENT_SECRET= + +# Zoom +NEXT_PUBLIC_ZOOM_ENABLED= +ZOOM_CUSTOM_NAME= +ZOOM_CLIENT_ID= +ZOOM_CLIENT_SECRET= From 9868ab61c9c4deb4fdfac37bd3e14548c559bffa Mon Sep 17 00:00:00 2001 From: Sebastian Hierholzer Date: Thu, 7 Dec 2023 14:49:56 +0100 Subject: [PATCH 7/7] fix: typo --- .env.sample | 2 +- pages/api/v1/logins/index.ts | 2 +- types/enviornment.d.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.sample b/.env.sample index daf5c94..70576b3 100644 --- a/.env.sample +++ b/.env.sample @@ -192,7 +192,7 @@ IDS4_ISSUER= # Kakao NEXT_PUBLIC_KAKAO_ENABLED= -KAOKAO_CUSTOM_NAME= +KAKAO_CUSTOM_NAME= KAKAO_CLIENT_ID= KAKAO_CLIENT_SECRET= diff --git a/pages/api/v1/logins/index.ts b/pages/api/v1/logins/index.ts index b5efab8..ff58bc2 100644 --- a/pages/api/v1/logins/index.ts +++ b/pages/api/v1/logins/index.ts @@ -111,7 +111,7 @@ export function getLogins() { } // Kakao if (process.env.NEXT_PUBLIC_KAKAO_ENABLED === 'true') { - buttonAuths.push({method: 'kakao', name: process.env.KAOKAO_CUSTOM_NAME ?? 'Kakao'}); + buttonAuths.push({method: 'kakao', name: process.env.KAKAO_CUSTOM_NAME ?? 'Kakao'}); } // Keycloak if (process.env.NEXT_PUBLIC_KEYCLOAK_ENABLED === 'true') { diff --git a/types/enviornment.d.ts b/types/enviornment.d.ts index 0cfbe51..d88f257 100644 --- a/types/enviornment.d.ts +++ b/types/enviornment.d.ts @@ -200,7 +200,7 @@ declare global { // Kakao NEXT_PUBLIC_KAKAO_ENABLED?: string; - KAOKAO_CUSTOM_NAME?: string; + KAKAO_CUSTOM_NAME?: string; KAKAO_CLIENT_ID?: string; KAKAO_CLIENT_SECRET?: string;