mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-12 01:02:17 +00:00
fix server action
This commit is contained in:
@@ -6,8 +6,10 @@ import {
|
|||||||
symbolValidator,
|
symbolValidator,
|
||||||
upperCaseValidator,
|
upperCaseValidator,
|
||||||
} from "@/helpers/validators";
|
} from "@/helpers/validators";
|
||||||
import { sendPassword } from "@/lib/server/password";
|
import {
|
||||||
import { checkSessionAndSetPassword } from "@/lib/zitadel";
|
checkSessionAndSetPassword,
|
||||||
|
sendPassword,
|
||||||
|
} from "@/lib/server/password";
|
||||||
import { create } from "@zitadel/client";
|
import { create } from "@zitadel/client";
|
||||||
import { ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
|
import { ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
|
||||||
import { PasswordComplexitySettings } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb";
|
import { PasswordComplexitySettings } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb";
|
||||||
|
|||||||
@@ -6,23 +6,30 @@ import {
|
|||||||
} from "@/lib/server/cookie";
|
} from "@/lib/server/cookie";
|
||||||
import {
|
import {
|
||||||
getLoginSettings,
|
getLoginSettings,
|
||||||
|
getSession,
|
||||||
getUserByID,
|
getUserByID,
|
||||||
listAuthenticationMethodTypes,
|
listAuthenticationMethodTypes,
|
||||||
listUsers,
|
listUsers,
|
||||||
passwordReset,
|
passwordReset,
|
||||||
setPassword,
|
setPassword,
|
||||||
|
setUserPassword,
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import { create } from "@zitadel/client";
|
import { create } from "@zitadel/client";
|
||||||
|
import { createUserServiceClient } from "@zitadel/client/v2";
|
||||||
|
import { createServerTransport } from "@zitadel/node";
|
||||||
import {
|
import {
|
||||||
Checks,
|
Checks,
|
||||||
ChecksSchema,
|
ChecksSchema,
|
||||||
} from "@zitadel/proto/zitadel/session/v2/session_service_pb";
|
} from "@zitadel/proto/zitadel/session/v2/session_service_pb";
|
||||||
import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||||
import { User, UserState } from "@zitadel/proto/zitadel/user/v2/user_pb";
|
import { User, UserState } from "@zitadel/proto/zitadel/user/v2/user_pb";
|
||||||
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
import {
|
||||||
|
AuthenticationMethodType,
|
||||||
|
SetPasswordRequestSchema,
|
||||||
|
} from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
import { getNextUrl } from "../client";
|
import { getNextUrl } from "../client";
|
||||||
import { getSessionCookieByLoginName } from "../cookies";
|
import { getSessionCookieById, getSessionCookieByLoginName } from "../cookies";
|
||||||
|
|
||||||
type ResetPasswordCommand = {
|
type ResetPasswordCommand = {
|
||||||
loginName: string;
|
loginName: string;
|
||||||
@@ -302,5 +309,99 @@ export async function changePassword(command: {
|
|||||||
}
|
}
|
||||||
const userId = user.userId;
|
const userId = user.userId;
|
||||||
|
|
||||||
return setPassword(userId, command.password, user, command.code);
|
return setUserPassword(userId, command.password, user, command.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
type CheckSessionAndSetPasswordCommand = {
|
||||||
|
sessionId: string;
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function checkSessionAndSetPassword({
|
||||||
|
sessionId,
|
||||||
|
password,
|
||||||
|
}: CheckSessionAndSetPasswordCommand) {
|
||||||
|
const sessionCookie = await getSessionCookieById({ sessionId });
|
||||||
|
|
||||||
|
const { session } = await getSession({
|
||||||
|
sessionId: sessionCookie.id,
|
||||||
|
sessionToken: sessionCookie.token,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!session || !session.factors?.user?.id) {
|
||||||
|
return { error: "Could not load session" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = create(SetPasswordRequestSchema, {
|
||||||
|
userId: session.factors.user.id,
|
||||||
|
newPassword: {
|
||||||
|
password,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// check if the user has no password set in order to set a password
|
||||||
|
const authmethods = await listAuthenticationMethodTypes(
|
||||||
|
session.factors.user.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!authmethods) {
|
||||||
|
return { error: "Could not load auth methods" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const requiredAuthMethodsForForceMFA = [
|
||||||
|
AuthenticationMethodType.OTP_EMAIL,
|
||||||
|
AuthenticationMethodType.OTP_SMS,
|
||||||
|
AuthenticationMethodType.TOTP,
|
||||||
|
AuthenticationMethodType.U2F,
|
||||||
|
];
|
||||||
|
|
||||||
|
const hasNoMFAMethods = requiredAuthMethodsForForceMFA.every(
|
||||||
|
(method) => !authmethods.authMethodTypes.includes(method),
|
||||||
|
);
|
||||||
|
|
||||||
|
const loginSettings = await getLoginSettings(
|
||||||
|
session.factors.user.organizationId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const forceMfa = !!(
|
||||||
|
loginSettings?.forceMfa || loginSettings?.forceMfaLocalOnly
|
||||||
|
);
|
||||||
|
|
||||||
|
// if the user has no MFA but MFA is enforced, we can set a password otherwise we use the token of the user
|
||||||
|
if (forceMfa && hasNoMFAMethods) {
|
||||||
|
return setPassword(payload).catch((error) => {
|
||||||
|
// throw error if failed precondition (ex. User is not yet initialized)
|
||||||
|
if (error.code === 9 && error.message) {
|
||||||
|
return { error: "Failed precondition" };
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const myUserService = (sessionToken: string) => {
|
||||||
|
return createUserServiceClient(
|
||||||
|
createServerTransport(sessionToken, {
|
||||||
|
baseUrl: process.env.ZITADEL_API_URL!,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const selfService = await myUserService(`${sessionCookie.token}`);
|
||||||
|
|
||||||
|
return selfService
|
||||||
|
.setPassword(
|
||||||
|
{
|
||||||
|
userId: session.factors.user.id,
|
||||||
|
newPassword: { password, changeRequired: false },
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
if (error.code === 7) {
|
||||||
|
return { error: "Session is not valid." };
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import { RequestChallenges } from "@zitadel/proto/zitadel/session/v2/challenge_p
|
|||||||
import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
|
import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
|
||||||
import { IDPInformation } from "@zitadel/proto/zitadel/user/v2/idp_pb";
|
import { IDPInformation } from "@zitadel/proto/zitadel/user/v2/idp_pb";
|
||||||
import {
|
import {
|
||||||
AuthenticationMethodType,
|
|
||||||
RetrieveIdentityProviderIntentRequest,
|
RetrieveIdentityProviderIntentRequest,
|
||||||
|
SetPasswordRequest,
|
||||||
SetPasswordRequestSchema,
|
SetPasswordRequestSchema,
|
||||||
VerifyPasskeyRegistrationRequest,
|
VerifyPasskeyRegistrationRequest,
|
||||||
VerifyU2FRegistrationRequest,
|
VerifyU2FRegistrationRequest,
|
||||||
@@ -39,7 +39,6 @@ import {
|
|||||||
UserState,
|
UserState,
|
||||||
} from "@zitadel/proto/zitadel/user/v2/user_pb";
|
} from "@zitadel/proto/zitadel/user/v2/user_pb";
|
||||||
import { unstable_cacheLife as cacheLife } from "next/cache";
|
import { unstable_cacheLife as cacheLife } from "next/cache";
|
||||||
import { getSessionCookieById } from "./cookies";
|
|
||||||
import { PROVIDER_MAPPING } from "./idp";
|
import { PROVIDER_MAPPING } from "./idp";
|
||||||
|
|
||||||
const transport = createServerTransport(
|
const transport = createServerTransport(
|
||||||
@@ -538,7 +537,7 @@ export async function passwordReset(
|
|||||||
* @param code optional if the password should be set with a code (reset), no code for initial setup of password
|
* @param code optional if the password should be set with a code (reset), no code for initial setup of password
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export async function setPassword(
|
export async function setUserPassword(
|
||||||
userId: string,
|
userId: string,
|
||||||
password: string,
|
password: string,
|
||||||
user: User,
|
user: User,
|
||||||
@@ -584,97 +583,8 @@ export async function setPassword(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckSessionAndSetPasswordCommand = {
|
export async function setPassword(payload: SetPasswordRequest) {
|
||||||
sessionId: string;
|
return userService.setPassword(payload, {});
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function checkSessionAndSetPassword({
|
|
||||||
sessionId,
|
|
||||||
password,
|
|
||||||
}: CheckSessionAndSetPasswordCommand) {
|
|
||||||
const sessionCookie = await getSessionCookieById({ sessionId });
|
|
||||||
|
|
||||||
const { session } = await getSession({
|
|
||||||
sessionId: sessionCookie.id,
|
|
||||||
sessionToken: sessionCookie.token,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!session || !session.factors?.user?.id) {
|
|
||||||
return { error: "Could not load session" };
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload = create(SetPasswordRequestSchema, {
|
|
||||||
userId: session.factors.user.id,
|
|
||||||
newPassword: {
|
|
||||||
password,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// check if the user has no password set in order to set a password
|
|
||||||
const authmethods = await listAuthenticationMethodTypes(
|
|
||||||
session.factors.user.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!authmethods) {
|
|
||||||
return { error: "Could not load auth methods" };
|
|
||||||
}
|
|
||||||
|
|
||||||
const requiredAuthMethodsForForceMFA = [
|
|
||||||
AuthenticationMethodType.OTP_EMAIL,
|
|
||||||
AuthenticationMethodType.OTP_SMS,
|
|
||||||
AuthenticationMethodType.TOTP,
|
|
||||||
AuthenticationMethodType.U2F,
|
|
||||||
];
|
|
||||||
|
|
||||||
const hasNoMFAMethods = requiredAuthMethodsForForceMFA.every(
|
|
||||||
(method) => !authmethods.authMethodTypes.includes(method),
|
|
||||||
);
|
|
||||||
|
|
||||||
const loginSettings = await getLoginSettings(
|
|
||||||
session.factors.user.organizationId,
|
|
||||||
);
|
|
||||||
const forceMfa = !!(
|
|
||||||
loginSettings?.forceMfa || loginSettings?.forceMfaLocalOnly
|
|
||||||
);
|
|
||||||
|
|
||||||
// if the user has no MFA but MFA is enforced, we can set a password otherwise we use the token of the user
|
|
||||||
if (forceMfa && hasNoMFAMethods) {
|
|
||||||
return userService.setPassword(payload, {}).catch((error) => {
|
|
||||||
// throw error if failed precondition (ex. User is not yet initialized)
|
|
||||||
if (error.code === 9 && error.message) {
|
|
||||||
return { error: "Failed precondition" };
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const myUserService = (sessionToken: string) => {
|
|
||||||
return createUserServiceClient(
|
|
||||||
createServerTransport(sessionToken, {
|
|
||||||
baseUrl: process.env.ZITADEL_API_URL!,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const selfService = await myUserService(`${sessionCookie.token}`);
|
|
||||||
|
|
||||||
return selfService
|
|
||||||
.setPassword(
|
|
||||||
{
|
|
||||||
userId: session.factors.user.id,
|
|
||||||
newPassword: { password, changeRequired: false },
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
)
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
if (error.code === 7) {
|
|
||||||
return { error: "Session is not valid." };
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user