mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
Return error when adding auth contact channel in use by another account
Some checks failed
Runs E2E API Tests / build (20.x) (push) Has been cancelled
Runs E2E API Tests / build (22.x) (push) Has been cancelled
Lint & build / lint_and_build (20.x) (push) Has been cancelled
Lint & build / lint_and_build (22.x) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
Some checks failed
Runs E2E API Tests / build (20.x) (push) Has been cancelled
Runs E2E API Tests / build (22.x) (push) Has been cancelled
Lint & build / lint_and_build (20.x) (push) Has been cancelled
Lint & build / lint_and_build (22.x) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
This commit is contained in:
parent
ac9a85317c
commit
01eddd99a1
@ -129,12 +129,29 @@ export const contactChannelsCrudHandlers = createLazyProxy(() => createCrudHandl
|
||||
}
|
||||
|
||||
const updatedContactChannel = await prismaClient.$transaction(async (tx) => {
|
||||
await ensureContactChannelExists(tx, {
|
||||
const existingContactChannel = await ensureContactChannelExists(tx, {
|
||||
projectId: auth.project.id,
|
||||
userId: params.user_id,
|
||||
contactChannelId: params.contact_channel_id || throwErr("Missing contact channel id"),
|
||||
});
|
||||
|
||||
// if usedForAuth is set to true, make sure no other account uses this channel for auth
|
||||
if (data.used_for_auth) {
|
||||
const existingWithSameChannel = await tx.contactChannel.findUnique({
|
||||
where: {
|
||||
projectId_type_value_usedForAuth: {
|
||||
projectId: auth.project.id,
|
||||
type: data.type !== undefined ? crudContactChannelTypeToPrisma(data.type) : existingContactChannel.type,
|
||||
value: data.value !== undefined ? data.value : existingContactChannel.value,
|
||||
usedForAuth: 'TRUE',
|
||||
},
|
||||
},
|
||||
});
|
||||
if (existingWithSameChannel && existingWithSameChannel.id !== existingContactChannel.id) {
|
||||
throw new KnownErrors.ContactChannelAlreadyUsedForAuthBySomeoneElse(data.type ?? prismaContactChannelTypeToCrud(existingContactChannel.type));
|
||||
}
|
||||
}
|
||||
|
||||
if (data.is_primary) {
|
||||
// mark all other channels as not primary
|
||||
await tx.contactChannel.updateMany({
|
||||
@ -215,3 +232,12 @@ export const contactChannelsCrudHandlers = createLazyProxy(() => createCrudHandl
|
||||
};
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
function crudContactChannelTypeToPrisma(type: "email") {
|
||||
return typedToUppercase(type);
|
||||
}
|
||||
|
||||
function prismaContactChannelTypeToCrud(type: "EMAIL") {
|
||||
return typedToLowercase(type);
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { ProxiedOAuthProviderType, StandardOAuthProviderType } from "@prisma/client";
|
||||
import { KnownErrors } from "@stackframe/stack-shared";
|
||||
import { ProjectsCrud } from "@stackframe/stack-shared/dist/interface/crud/projects";
|
||||
import { StatusError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { ProviderType, sharedProviders, standardProviders } from "@stackframe/stack-shared/dist/utils/oauth";
|
||||
import { typedToUppercase } from "@stackframe/stack-shared/dist/utils/strings";
|
||||
import { listUserTeamPermissions } from "./permissions";
|
||||
import { PrismaTransaction } from "./types";
|
||||
import { StatusError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { typedToUppercase } from "@stackframe/stack-shared/dist/utils/strings";
|
||||
|
||||
|
||||
async function _getTeamMembership(
|
||||
@ -198,4 +198,6 @@ export async function ensureContactChannelExists(
|
||||
if (!contactChannel) {
|
||||
throw new StatusError(StatusError.BadRequest, 'Contact channel not found');
|
||||
}
|
||||
|
||||
return contactChannel;
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { StackAssertionError, StatusError, throwErr } from "./utils/errors";
|
||||
import { identityArgs } from "./utils/functions";
|
||||
import { Json } from "./utils/json";
|
||||
import { filterUndefined } from "./utils/objects";
|
||||
import { deindent } from "./utils/strings";
|
||||
|
||||
export type KnownErrorJson = {
|
||||
@ -1187,6 +1186,17 @@ const OAuthProviderAccessDenied = createKnownErrorConstructor(
|
||||
() => [] as const,
|
||||
);
|
||||
|
||||
const ContactChannelAlreadyUsedForAuthBySomeoneElse = createKnownErrorConstructor(
|
||||
KnownError,
|
||||
"CONTACT_CHANNEL_ALREADY_USED_FOR_AUTH_BY_SOMEONE_ELSE",
|
||||
(type: "email") => [
|
||||
400,
|
||||
`This ${type} is already used for authentication by another account.`,
|
||||
{ type },
|
||||
] as const,
|
||||
(json) => [json.type] as const,
|
||||
);
|
||||
|
||||
export type KnownErrors = {
|
||||
[K in keyof typeof KnownErrors]: InstanceType<typeof KnownErrors[K]>;
|
||||
};
|
||||
@ -1283,6 +1293,7 @@ export const KnownErrors = {
|
||||
InvalidAuthorizationCode,
|
||||
TeamPermissionNotFound,
|
||||
OAuthProviderAccessDenied,
|
||||
ContactChannelAlreadyUsedForAuthBySomeoneElse,
|
||||
} satisfies Record<string, KnownErrorConstructor<any, any>>;
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user