Remove Discord and Google OAuth verification from CMS
- Remove Discord + Google OAuth providers from NextAuth config - Remove social login buttons (Discord/Google) from login form - Delete link-discord.ts action and discord-verify-form.tsx component - Simplify verify page to email-only verification flow - Remove connections settings page and its link from settings - Clean env.ts, .env, .env.example of Discord/Google client vars - Remove discordUrl social icon from register, login, and homepage - Clean translation files: remove continueWithDiscord, continueWithGoogle, connections keys
This commit is contained in:
1 parent
f7f6e09174
commit
7f8d083763
36 files changed
+34
-994
No files matched your search
+1
-133
@@ -1,7 +1,5 @@
|
||||
import NextAuth from "next-auth";
|
||||
import Credentials from "next-auth/providers/credentials";
|
||||
import Discord from "next-auth/providers/discord";
|
||||
import Google from "next-auth/providers/google";
|
||||
import { env } from "@/env";
|
||||
import { LaravelEncrypter } from "@/lib/auth/laravel-encrypter";
|
||||
import { getCachedJwtVersion } from "@/lib/auth/jwt-version-cache";
|
||||
@@ -144,67 +142,8 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||
};
|
||||
},
|
||||
}),
|
||||
// OAuth providers — enabled only when both id + secret are configured.
|
||||
...(env.DISCORD_CLIENT_ID && env.DISCORD_CLIENT_SECRET
|
||||
? [
|
||||
Discord({
|
||||
clientId: env.DISCORD_CLIENT_ID,
|
||||
clientSecret: env.DISCORD_CLIENT_SECRET,
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
...(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET
|
||||
? [
|
||||
Google({
|
||||
clientId: env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: env.GOOGLE_CLIENT_SECRET,
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
],
|
||||
callbacks: {
|
||||
async signIn({ user, account }) {
|
||||
if (account?.provider === "credentials") return true;
|
||||
|
||||
const requireLink = await siteSettings.getBool(
|
||||
"oauth_require_link",
|
||||
false,
|
||||
);
|
||||
|
||||
// Always allow explicitly linked accounts.
|
||||
if (account?.provider === "discord" && account.providerAccountId) {
|
||||
try {
|
||||
const linked = await prisma.socialAccounts.findUnique({
|
||||
where: {
|
||||
provider_providerId: {
|
||||
provider: "discord",
|
||||
providerId: account.providerAccountId,
|
||||
},
|
||||
},
|
||||
select: { userId: true },
|
||||
});
|
||||
if (linked) return true;
|
||||
} catch {
|
||||
return "/login?error=Unavailable";
|
||||
}
|
||||
}
|
||||
|
||||
// Email-based binding: only allowed when oauth_require_link is disabled
|
||||
// AND the matched account does NOT have 2FA enabled (account takeover guard).
|
||||
if (!requireLink && user.email) {
|
||||
try {
|
||||
const dbUser = await prisma.user.findFirst({
|
||||
where: { mail: user.email, twoFactorConfirmedAt: null },
|
||||
select: { id: true },
|
||||
});
|
||||
if (dbUser) return true;
|
||||
} catch {
|
||||
return "/login?error=Unavailable";
|
||||
}
|
||||
}
|
||||
|
||||
return "/login?error=NoAccount";
|
||||
},
|
||||
async jwt({ token, user, account }) {
|
||||
if (user) {
|
||||
token.jwtVersion =
|
||||
@@ -216,81 +155,10 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||
|
||||
if (user && account?.provider === "credentials") {
|
||||
token.rank = (user as { rank?: number }).rank;
|
||||
token.sub = String((user as { id?: string }).id);
|
||||
return token;
|
||||
}
|
||||
|
||||
// OAuth account linking only when establishing a session — not on
|
||||
// every subsequent request (avoids siteSettings + DB on each hit).
|
||||
if (user || account) {
|
||||
const requireLink = await siteSettings.getBool(
|
||||
"oauth_require_link",
|
||||
false,
|
||||
);
|
||||
|
||||
// Try Discord ID via SocialAccounts (always allowed, even when requireLink is true).
|
||||
if (
|
||||
!token.sub &&
|
||||
account?.provider === "discord" &&
|
||||
account.providerAccountId
|
||||
) {
|
||||
try {
|
||||
const linked = await prisma.socialAccounts.findUnique({
|
||||
where: {
|
||||
provider_providerId: {
|
||||
provider: "discord",
|
||||
providerId: account.providerAccountId,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (linked) {
|
||||
const dbUser = await prisma.user.findUnique({
|
||||
where: { id: Number(linked.userId) },
|
||||
select: {
|
||||
id: true,
|
||||
rank: true,
|
||||
username: true,
|
||||
websiteJwtVersion: true,
|
||||
},
|
||||
});
|
||||
if (dbUser) {
|
||||
token.sub = String(dbUser.id);
|
||||
token.rank = dbUser.rank;
|
||||
token.name = dbUser.username;
|
||||
token.jwtVersion = dbUser.websiteJwtVersion;
|
||||
token.jwtCheckedAt = Date.now();
|
||||
return token;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// leave token as-is on lookup failure
|
||||
}
|
||||
}
|
||||
|
||||
// Email-based binding: only when requireLink is off AND account has no 2FA.
|
||||
if (!requireLink && user?.email && !token.sub) {
|
||||
try {
|
||||
const dbUser = await prisma.user.findFirst({
|
||||
where: { mail: user.email, twoFactorConfirmedAt: null },
|
||||
select: {
|
||||
id: true,
|
||||
rank: true,
|
||||
username: true,
|
||||
websiteJwtVersion: true,
|
||||
},
|
||||
});
|
||||
if (dbUser) {
|
||||
token.sub = String(dbUser.id);
|
||||
token.rank = dbUser.rank;
|
||||
token.name = dbUser.username;
|
||||
token.jwtVersion = dbUser.websiteJwtVersion;
|
||||
token.jwtCheckedAt = Date.now();
|
||||
}
|
||||
} catch {
|
||||
// leave token as-is on lookup failure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Re-check jwt version at most once per minute (memory/Redis cached).
|
||||
if (token.sub && !token.invalid) {
|
||||
const lastCheck =
|
||||
|
||||
Reference in new issue
Block a user