fix server action

This commit is contained in:
Max Peintner
2024-12-12 18:13:49 +01:00
parent 8dc0e14f13
commit 16822afe83
3 changed files with 112 additions and 99 deletions

View File

@@ -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";

View File

@@ -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;
});
}
} }

View File

@@ -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;
});
}
} }
/** /**