mirror of
https://github.com/andwati/dokploy.git
synced 2026-10-18 03:02:56 +00:00
Merge pull request #5071 from Dokploy/fix/scim-sso-account-linking
fix(auth): link SCIM-provisioned users through SSO and surface sign-in errors
This commit is contained in:
@@ -13,6 +13,7 @@ export function SignInWithGithub() {
|
|||||||
try {
|
try {
|
||||||
const { error } = await authClient.signIn.social({
|
const { error } = await authClient.signIn.social({
|
||||||
provider: "github",
|
provider: "github",
|
||||||
|
errorCallbackURL: "/",
|
||||||
});
|
});
|
||||||
if (error) {
|
if (error) {
|
||||||
toast.error(error.message);
|
toast.error(error.message);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export function SignInWithGoogle() {
|
|||||||
try {
|
try {
|
||||||
const { error } = await authClient.signIn.social({
|
const { error } = await authClient.signIn.social({
|
||||||
provider: "google",
|
provider: "google",
|
||||||
|
errorCallbackURL: "/",
|
||||||
});
|
});
|
||||||
if (error) {
|
if (error) {
|
||||||
toast.error(error.message);
|
toast.error(error.message);
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export function SignInWithSSO({
|
|||||||
const { data, error } = await authClient.signIn.sso({
|
const { data, error } = await authClient.signIn.sso({
|
||||||
email: values.email,
|
email: values.email,
|
||||||
callbackURL: "/dashboard/home",
|
callbackURL: "/dashboard/home",
|
||||||
|
errorCallbackURL: "/",
|
||||||
});
|
});
|
||||||
if (error) {
|
if (error) {
|
||||||
toast.error(error.message ?? "Failed to sign in with SSO");
|
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 type { GetServerSidePropsContext } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
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 { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { z } from "zod";
|
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) => {
|
const onSubmit = async (values: LoginForm) => {
|
||||||
setIsLoginLoading(true);
|
setIsLoginLoading(true);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -77,6 +77,9 @@ const createBetterAuth = () =>
|
|||||||
...(!IS_CLOUD ? ["/verify-email"] : []),
|
...(!IS_CLOUD ? ["/verify-email"] : []),
|
||||||
],
|
],
|
||||||
secret: betterAuthSecret,
|
secret: betterAuthSecret,
|
||||||
|
onAPIError: {
|
||||||
|
errorURL: "/",
|
||||||
|
},
|
||||||
...(!IS_CLOUD
|
...(!IS_CLOUD
|
||||||
? {
|
? {
|
||||||
advanced: {
|
advanced: {
|
||||||
@@ -165,6 +168,9 @@ const createBetterAuth = () =>
|
|||||||
user: {
|
user: {
|
||||||
create: {
|
create: {
|
||||||
before: async (_user, context) => {
|
before: async (_user, context) => {
|
||||||
|
if (context?.path.includes("/scim")) {
|
||||||
|
return { data: { emailVerified: true } };
|
||||||
|
}
|
||||||
if (!IS_CLOUD) {
|
if (!IS_CLOUD) {
|
||||||
const xDokployToken =
|
const xDokployToken =
|
||||||
context?.request?.headers?.get("x-dokploy-token");
|
context?.request?.headers?.get("x-dokploy-token");
|
||||||
@@ -197,8 +203,7 @@ const createBetterAuth = () =>
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const isSSORequest = context?.path.includes("/sso");
|
const isSSORequest = context?.path.includes("/sso");
|
||||||
const isSCIMRequest = context?.path.includes("/scim");
|
if (isSSORequest) {
|
||||||
if (isSSORequest || isSCIMRequest) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const isAdminPresent = await db.query.member.findFirst({
|
const isAdminPresent = await db.query.member.findFirst({
|
||||||
@@ -251,6 +256,20 @@ const createBetterAuth = () =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isSCIMRequest) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user