mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-10-15 13:21:11 +00:00
fix(auth): link SCIM-provisioned users through SSO and surface sign-in errors
SCIM provisioning created users with emailVerified: false, so better-auth refused to link the matching SSO account (account_not_linked), and the resulting redirect landed on better-auth's bare /error page with no actionable feedback. - Mark SCIM-provisioned users as email-verified at creation time, same trust rationale already applied to admin-invited credential users. - Sync a SCIM user's membership role to the organization's configured default role instead of the hardcoded "member" the SCIM plugin creates. - Set errorCallbackURL/onAPIError.errorURL to the sign-in page so failed SSO/OAuth callbacks land back on Dokploy's UI instead of better-auth's generic error page. - Read the error query param on the sign-in page and show it through the existing AlertBlock instead of failing silently. Fixes #4973
This commit is contained in:
@@ -13,6 +13,7 @@ export function SignInWithGithub() {
|
||||
try {
|
||||
const { error } = await authClient.signIn.social({
|
||||
provider: "github",
|
||||
errorCallbackURL: "/",
|
||||
});
|
||||
if (error) {
|
||||
toast.error(error.message);
|
||||
|
||||
@@ -13,6 +13,7 @@ export function SignInWithGoogle() {
|
||||
try {
|
||||
const { error } = await authClient.signIn.social({
|
||||
provider: "google",
|
||||
errorCallbackURL: "/",
|
||||
});
|
||||
if (error) {
|
||||
toast.error(error.message);
|
||||
|
||||
@@ -50,6 +50,7 @@ export function SignInWithSSO({
|
||||
const { data, error } = await authClient.signIn.sso({
|
||||
email: values.email,
|
||||
callbackURL: "/dashboard/home",
|
||||
errorCallbackURL: "/",
|
||||
});
|
||||
if (error) {
|
||||
toast.error(error.message ?? "Failed to sign in with SSO");
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Fingerprint } from "lucide-react";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { type ReactElement, useState } from "react";
|
||||
import { type ReactElement, useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
@@ -85,6 +85,29 @@ export default function Home({ IS_CLOUD, enforceSSO }: Props) {
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const queryError = router.query.error;
|
||||
if (!queryError) return;
|
||||
|
||||
const raw = Array.isArray(queryError) ? queryError[0] : queryError;
|
||||
if (!raw) return;
|
||||
const normalized = raw.replace(/[+_]/g, " ").toLowerCase();
|
||||
|
||||
setError(
|
||||
normalized.includes("account not linked")
|
||||
? "This account already exists but isn't linked to that sign-in provider yet. Contact your administrator to link it."
|
||||
: normalized.includes("access denied")
|
||||
? "Access was denied by the identity provider."
|
||||
: "We couldn't complete sign-in. Please try again or contact your administrator.",
|
||||
);
|
||||
|
||||
const { error: _removed, ...rest } = router.query;
|
||||
router.replace({ pathname: router.pathname, query: rest }, undefined, {
|
||||
shallow: true,
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [router.query.error]);
|
||||
|
||||
const onSubmit = async (values: LoginForm) => {
|
||||
setIsLoginLoading(true);
|
||||
try {
|
||||
|
||||
@@ -77,6 +77,9 @@ const createBetterAuth = () =>
|
||||
...(!IS_CLOUD ? ["/verify-email"] : []),
|
||||
],
|
||||
secret: betterAuthSecret,
|
||||
onAPIError: {
|
||||
errorURL: "/",
|
||||
},
|
||||
...(!IS_CLOUD
|
||||
? {
|
||||
advanced: {
|
||||
@@ -165,6 +168,9 @@ const createBetterAuth = () =>
|
||||
user: {
|
||||
create: {
|
||||
before: async (_user, context) => {
|
||||
if (context?.path.includes("/scim")) {
|
||||
return { data: { emailVerified: true } };
|
||||
}
|
||||
if (!IS_CLOUD) {
|
||||
const xDokployToken =
|
||||
context?.request?.headers?.get("x-dokploy-token");
|
||||
@@ -197,8 +203,7 @@ const createBetterAuth = () =>
|
||||
}
|
||||
} else {
|
||||
const isSSORequest = context?.path.includes("/sso");
|
||||
const isSCIMRequest = context?.path.includes("/scim");
|
||||
if (isSSORequest || isSCIMRequest) {
|
||||
if (isSSORequest) {
|
||||
return;
|
||||
}
|
||||
const isAdminPresent = await db.query.member.findFirst({
|
||||
@@ -251,6 +256,20 @@ const createBetterAuth = () =>
|
||||
}
|
||||
|
||||
if (isSCIMRequest) {
|
||||
const membership = await db.query.member.findFirst({
|
||||
where: eq(schema.member.userId, user.id),
|
||||
});
|
||||
if (membership) {
|
||||
const defaultRole = await resolveOrganizationDefaultRole(
|
||||
membership.organizationId,
|
||||
);
|
||||
if (defaultRole !== membership.role) {
|
||||
await db
|
||||
.update(schema.member)
|
||||
.set({ role: defaultRole })
|
||||
.where(eq(schema.member.id, membership.id));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user