mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-06 20:22:14 +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 {
|
||||
|
||||
Reference in New Issue
Block a user